/*  SSCatalystDocument.m
 *  MESLib
 * 
 * Copyright 2003-2009 Starline Software Ltd
 * 
 * This file is part of MESLib.
 * 
 * MESLib is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * MESLib is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with MESLib. If not, see <http://www.gnu.org/licenses/>.
 */

//  Created by Tobias Haughton on Tue Jan 21 2003.
//

#import "SSCatalystDocument.h"
#import "SSSharedNewEBPanelController.h"
#import "SSEventView.h"
#import "SSEBPanelController.h"
#import "SSCommentPanelController.h"
#import "SSNeedsDiamondsPanelController.h"
#import "SSCatalystAppController.h"

@implementation SSCatalystDocument

- (id)init
{
   if (self = [super init]) {
      NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
      documentData = [[SSCatalystDocumentData alloc] initWithDocument:self];
      missingFiles = [[NSMutableArray alloc] init];
      actorOrder = [[NSMutableArray alloc] init];
      skippedImportedEvents = [[NSMutableArray alloc] init];
      objectFilterController = [[SSObjectFilterController alloc] init];
      outputString = [[NSString string] retain];

      //add the standard poison words
      int i = 0;
      NSMutableArray *defaultPoisonWords = [[NSUserDefaults standardUserDefaults] objectForKey:SSDefaultPoisonWordListKey];
      while(i < [defaultPoisonWords count]) {
         [self addPoisonWord:[[[SSPoisonWord alloc] initWithPoisonWord:[[defaultPoisonWords objectAtIndex:i] objectForKey:@"word"]
                                                        warningMessage:[[defaultPoisonWords objectAtIndex:i] objectForKey:@"warning"]] autorelease]];
         i++;
      }

      [nc addObserver:self
             selector:@selector(setTableViewRowHeight)
                 name:@"SSUpdateEBDisplay"
               object:nil];
      [nc addObserver:self
             selector:@selector(redisplay)
                 name:@"SSRedisplayChart"
               object:nil];
      [nc addObserver:self
             selector:@selector(setLegendViewDisplay)
                 name:@"SSLegendDisplayChanged"
               object:nil];
      [nc addObserver:self
             selector:@selector(addJumpMapToChartView)
                 name:@"SSAddJumpMapToChart"
               object:nil];
      [nc addObserver:self
             selector:@selector(removeJumpMapFromChartView)
                 name:@"SSRemoveJumpMapFromChart"
               object:nil];
      
   }

   return self;
}

- (NSString *)windowNibName {
    return @"CatalystDocWindow";
}

//****saving and loading

- (void)saveDocumentWithDelegate:(id)delegate didSaveSelector:(SEL)didSaveSelector contextInfo:(void *)contextInfo
{
   [super saveDocumentWithDelegate:self
                        didSaveSelector:@selector(document:didSave:contextInfo:)
                            contextInfo:contextInfo];
}

- (void)document:(NSDocument *)doc didSave:(BOOL)didSave contextInfo:(void *)contextInfo
{
   if(didSave) {
       //user just saved so write the file name to defaults
       [[NSUserDefaults standardUserDefaults] setObject:[self fileName]
                                                  forKey:SSLastSavedFileKey];
   }
}

- (NSFileWrapper *)fileWrapperRepresentationOfType:(NSString *)type
{
   //called when user selects save
   NSFileWrapper *file;
   if([type isEqualToString:@"Catalyst Document"]) {
      //saving a normal doc
      file = [self catalystFileWrapperRepresentation:nil]; //pass nil so that it will know that this is the top level directory
   } else {
      //saving a compressed doc
      file = [self catalystCompressedFileWrapperRepresentation];
   }

   return file;
}

- (NSFileWrapper *)catalystFileWrapperRepresentation:(NSMutableDictionary *)dict
{
   //called to produce a "normal" file (directory with _structure.xml)

   NSFileWrapper *directoryWrapper = [[[NSFileWrapper alloc] initDirectoryWithFileWrappers:nil] autorelease];
   if(!dict) {
      //it should be the top level file
      dict = [documentData structureDictionary]; 
   } else {
      //we only want to set file name for sub directories
      [directoryWrapper setPreferredFilename:[dict objectForKey:@"fileName"]];
   }
   
   NSEnumerator *eventEnumerator = [[dict objectForKey:@"referencedEventBlocks"] objectEnumerator];
   SSEventObject *eventObject;
   NSEnumerator *commentEnumerator = [[dict objectForKey:@"comments"] objectEnumerator];
   SSCatalystComment *commentObject;
   NSEnumerator *subDirEnumerator = [[dict objectForKey:@"subDirectories"] objectEnumerator];
   NSMutableDictionary *subDirObject;
   NSEnumerator *diamondEnumerator = [[dict objectForKey:@"needsDiamonds"] objectEnumerator];
   SSNeedsDiamond *diamondObject;
   NSEnumerator *poisonWordEnumerator = [[dict objectForKey:@"poisonWords"] objectEnumerator];
   SSPoisonWord *poisonWordObject;
   NSString *structureString = [NSString string];
   NSString *actorOrderString = @"\t<actor_order>\n";
   NSString *diamondString = @"<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n";
   NSMutableArray *usedActorNames = [NSMutableArray array]; //when we write a row number for an actor we add the name to this array so that they are not duplicated

   //build the _structure file
   structureString = [structureString stringByAppendingString:[NSString stringWithFormat:@"<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n\n<meschart unique_id=\"%@\">\n\t<chart_information>\n\t\t<title>%@</title>\n\t\t<author>%@</author>\n\t\t<chart_date>\n\t\t\t<year>%@</year>\n\t\t\t<month>%@</month>\n\t\t\t<day>%@</day>\n\t\t</chart_date>\n\t\t<projectFolder>%@</projectFolder>\n\t\t<white_color_designation>%@</white_color_designation>\n\t\t<yellow_color_designation>%@</yellow_color_designation>\n\t\t<green_color_designation>%@</green_color_designation>\n\t\t<light_blue_color_designation>%@</light_blue_color_designation>\n\t\t<blue_color_designation>%@</blue_color_designation>\n\t\t<purple_color_designation>%@</purple_color_designation>\n\t\t<orange_color_designation>%@</orange_color_designation>\n\t\t<red_color_designation>%@</red_color_designation>\n\t\t<diamond_UDValue_1>%@</diamond_UDValue_1>\n\t\t<diamond_UDValue_2>%@</diamond_UDValue_2>\n\t\t<diamond_UDValue_3>%@</diamond_UDValue_3>\n\t\t<diamond_UDValue_4>%@</diamond_UDValue_4>\n\t\t<diamond_UDValue_5>%@</diamond_UDValue_5>\n\t\t<diamond_UDValue_6>%@</diamond_UDValue_6>\n\t\t<diamond_option_UDValue_1>%@</diamond_option_UDValue_1>\n\t\t<diamond_option_UDValue_2>%@</diamond_option_UDValue_2>\n\t\t<diamond_option_UDValue_3>%@</diamond_option_UDValue_3>\n\t\t<diamond_option_UDValue_4>%@</diamond_option_UDValue_4>\n\t\t<diamond_option_UDValue_5>%@</diamond_option_UDValue_5>\n\t\t<diamond_tradeoff_UDValue_1>%@</diamond_tradeoff_UDValue_1>\n\t\t<diamond_tradeoff_UDValue_2>%@</diamond_tradeoff_UDValue_2>\n\t\t<diamond_tradeoff_UDValue_3>%@</diamond_tradeoff_UDValue_3>\n\t\t<actor_column_width>%f</actor_column_width>\n\t\t<display_mode>%d</display_mode>\n\t\t<overlap_unit>%f</overlap_unit>\n\t\t<overlap_pixels_per_unit>%f</overlap_pixels_per_unit>",
      [dict objectForKey:@"unique_id"], [dict objectForKey:@"title"], [dict objectForKey:@"author"], [[dict objectForKey:@"chartDate"] descriptionWithCalendarFormat:@"%Y"],
      [[dict objectForKey:@"chartDate"] descriptionWithCalendarFormat:@"%m"], [[dict objectForKey:@"chartDate"] descriptionWithCalendarFormat:@"%d"], [self projectFolder],
      [self whiteColorDesignation], [self yellowColorDesignation], [self greenColorDesignation], [self lightBlueColorDesignation], [self blueColorDesignation],
      [self purpleColorDesignation], [self orangeColorDesignation], [self redColorDesignation], [documentData structureObjectForKey:@"diamondUDValue1"], [documentData structureObjectForKey:@"diamondUDValue2"], [documentData structureObjectForKey:@"diamondUDValue3"],
      [documentData structureObjectForKey:@"diamondUDValue4"], [documentData structureObjectForKey:@"diamondUDValue5"], [documentData structureObjectForKey:@"diamondUDValue6"],
      [documentData structureObjectForKey:@"diamondOptionUDValue1"], [documentData structureObjectForKey:@"diamondOptionUDValue2"], [documentData structureObjectForKey:@"diamondOptionUDValue3"],
      [documentData structureObjectForKey:@"diamondOptionUDValue4"], [documentData structureObjectForKey:@"diamondOptionUDValue5"],
      [documentData structureObjectForKey:@"diamondTradeoffUDValue1"], [documentData structureObjectForKey:@"diamondTradeoffUDValue2"], [documentData structureObjectForKey:@"diamondTradeoffUDValue3"],
      [self actorColumnWidth], [self displayMode], [self overlapUnit], [self overlapPixelsPerUnit]]];


   structureString = [structureString stringByAppendingString:@"\n\t\t<poison_word_list>"];
   while(poisonWordObject = [poisonWordEnumerator nextObject]) {
   	structureString = [structureString stringByAppendingString:[NSString stringWithFormat:@"\n\t\t\t<poison_word>\n\t\t\t\t<word>%@</word>\n\t\t\t\t<warning_message>%@</warning_message>\n\t\t\t</poison_word>",
           [poisonWordObject poisonWord], [poisonWordObject warningMessage]]];
   }
   structureString = [structureString stringByAppendingString:@"\n\t\t</poison_word_list>\n\t</chart_information>\n\n"];
   
   while (eventObject = [eventEnumerator nextObject]) {
      //get the event references
      structureString = [structureString stringByAppendingString:[NSString stringWithFormat:@"%@\n\n", [eventObject xmlStringReferenceRepresentation]]];

      if(![usedActorNames containsObject:[eventObject actor]]) {
         //only add the actor order if we haven't already
         actorOrderString = [actorOrderString stringByAppendingString:[NSString stringWithFormat:@"\t\t<actor_row %d>%@</actor>\n", [self dataSourceDictionaryIndexOfObject:[eventObject actor]
                                                                                                                                                                         inArray:@"actor"], [eventObject actor]]];
         [usedActorNames addObject:[eventObject actor]];
      }

      //create and add the event file while we are here
      [directoryWrapper addRegularFileWithContents:[[eventObject xmlStringRepresentation] dataUsingEncoding:NSUTF8StringEncoding
                                                                                       allowLossyConversion:YES] preferredFilename:[eventObject fileName]];
   }

   //close the actor order string and add it to the structure string
   actorOrderString = [actorOrderString stringByAppendingString:@"\t</actor_order>\n\n"];
   structureString = [structureString stringByAppendingString:actorOrderString];
   
   while (commentObject = [commentEnumerator nextObject]) {
      //get the comment data
      structureString = [structureString stringByAppendingString:[NSString stringWithFormat:@"%@\n\n", [commentObject xmlStringRepresentation]]];
   }

   structureString = [structureString stringByAppendingString:@"</mesbchart>"]; //close the structure file
   
   //add the structure file to the directory
   [directoryWrapper addRegularFileWithContents:[structureString dataUsingEncoding:NSUTF8StringEncoding
                                                              allowLossyConversion:YES] preferredFilename:@"_structure.xml"];

   //create the diamond file for this directory
   while (diamondObject = [diamondEnumerator nextObject]) {
      diamondString = [diamondString stringByAppendingString:[diamondObject xmlStringRepresentation]];
   }

   //add the diamond file to the directory
   [directoryWrapper addRegularFileWithContents:[diamondString dataUsingEncoding:NSUTF8StringEncoding
                                                              allowLossyConversion:YES] preferredFilename:@"diamond.xml"];

   while (subDirObject = [subDirEnumerator nextObject]) {
      //loop through our sub directories and add them to the wrapper
      [directoryWrapper addFileWrapper:[self catalystFileWrapperRepresentation:subDirObject]];
   }
   
   return directoryWrapper;
}

- (NSFileWrapper *)catalystCompressedFileWrapperRepresentation
{
   //called to produce a compressed file
   NSFileWrapper *file = [[[NSFileWrapper alloc] initRegularFileWithContents:[NSKeyedArchiver archivedDataWithRootObject:documentData]] autorelease];
   return file;
}
   
- (void)saveBackupCopy
{
   if([self fileName] != nil) {
      [self writeToFile:[[[self fileName] substringWithRange:NSMakeRange(0, ([[self fileName] length] - 4))] stringByAppendingString:@"~.mes"]
                 ofType:[self fileType]];
   }
}

//*****loading

- (BOOL)loadFileWrapperRepresentation:(NSFileWrapper *)wrapper ofType:(NSString *)docType
{
   BOOL loaded;
   //docType doesn't seem to be working, at least with directories created outside of Catalyst so we are testing whether
   //the wrapper is a directory or a normal file to choose the type
   if([wrapper isDirectory]) {
      //loading a normal doc
      [[self undoManager] disableUndoRegistration]; //disable undo while we load
      loaded = [self loadDocumentWithFileWrapper:wrapper];
      catalystDocument = YES; //work around for setting our type to catalyst document
      [[self undoManager] enableUndoRegistration]; //re enable undo
   } else {
      //loading a compressed doc
      loaded = [self loadCompressedDocumentWithFileWrapper:wrapper];
   }

   if([documentData importCanceled]) {
      //the user has canceled the load because of duplicates so return no
      return NO;
   } else {
      //user hasn't canceled but they may want to skip events
      NSEnumerator *enumerator = [skippedImportedEvents objectEnumerator];
      id anObject;

      while (anObject = [enumerator nextObject]) {
         [self deleteObject:anObject];
      }
      //remove all of the events in skippedImportedEvents
      [skippedImportedEvents removeAllObjects];
   }
   
   return loaded;
}

- (BOOL)loadDocumentWithFileWrapper:(NSFileWrapper *)wrapper
{
   //called when loading a directory
   NSDictionary *fileDictionary = [wrapper fileWrappers]; //all files in the loaded directory
   SSXMLParser *parser = [[SSXMLParser alloc] init];
   BOOL success = NO;
   NSFileWrapper *structure = [fileDictionary objectForKey:@"_structure.xml"];

   if(structure) {
      //there is a file called _structure.xml
      [documentData setStructureDictionary:[parser dictionaryWithStructureXMLData:[structure regularFileContents]
                                                                         fileName:[wrapper filename]
                                                                  actorOrderArray:&actorOrder //actorOrder is passed for each .mes directory we load once finished we use it to order our actor rows
                                                                       successful:&success]];
   }

   if(success) {
      //there was a _structure file and it was valid
      [self addFilesFromDictionary:fileDictionary
                      structureDict:[documentData structureDictionary]
                      appendAddress:[NSString string]];

      //convert the links
      [documentData convertAllLinksToConnectionObjects];
      [documentData convertAllDiamondOwners];
      [documentData convertAllCommentClosestEvents];

      if([missingFiles count] != 0) {
         //there are missing or invalid EBs
         NSEnumerator *enumerator = [missingFiles objectEnumerator];
         id anObject;
         NSString *missingFilesString = [NSString string];

         while (anObject = [enumerator nextObject]) {
            //build a string with all missing files
            missingFilesString = [missingFilesString stringByAppendingString:[NSString stringWithFormat:@"%@\n", anObject]];
         }
         //put up alert with missing file names
         NSRunAlertPanel(NSLocalizedString(@"Error", nil), [NSString stringWithFormat:@"%@\n%@", NSLocalizedString(@"MissingEB", nil), missingFilesString], @"OK", nil, nil);
      }

      NSString *structureString = [[[NSString alloc] initWithData:[structure regularFileContents] encoding:NSUTF8StringEncoding] autorelease];
      
      //order the actor rows
      [documentData orderActorsBasedOnArray:actorOrder];
      [documentData setActorColumnWidth:[parser actorColumnWidthFromXMLString:structureString]];

      [documentData setDisplayMode:[parser displayModeFromXMLString:structureString]];

      [documentData setOverlapUnit:[parser overlapUnitFromXMLString:structureString]];
      [documentData setOverlapPixelsPerUnit:[parser overlapPixelsPerUnitFromXMLString:structureString]];
      
      return YES; //we loaded successfully
   }

   [parser release];

   //no _structure file or _structure was missing, inform user
   NSRunAlertPanel(NSLocalizedString(@"Error", nil), NSLocalizedString(@"InvalidStructureFile", nil), @"OK", nil, nil);

   return NO; //_structure.xml was not valid or not present
}

- (BOOL)loadCompressedDocumentWithFileWrapper:(NSFileWrapper *)wrapper
{
   //called to load a compressed file
   [documentData release];
   documentData = [[NSKeyedUnarchiver unarchiveObjectWithData:[wrapper regularFileContents]] retain];
   [documentData setTheDocument:self];

   return YES;
}

- (void)importFiles
{
   //this method handles the importation of full .mes directories and single event block files 
   int result;
   NSArray *fileTypes = [NSArray arrayWithObjects:@"mes", @"xml", nil];
   NSOpenPanel *oPanel = [NSOpenPanel openPanel];
   [oPanel setAllowsMultipleSelection:YES];
   result = [oPanel runModalForDirectory:[[NSUserDefaults standardUserDefaults] stringForKey:SSDefaultProjectFolderKey]
                                    file:nil types:fileTypes];
   SSXMLParser *parser = [[SSXMLParser alloc] init];
   NSUndoManager* aManager = [self undoManager];
   
   if (result == NSOKButton) {
      NSArray *filesToOpen = [oPanel filenames];
      NSEnumerator *enumerator = [filesToOpen objectEnumerator];
      NSString *path;
      [aManager beginUndoGrouping];
      
      //check that files are in project folder and display warning if not
      if([[oPanel directory] rangeOfString:[self projectFolder]].location == NSNotFound) {
         //this is not the project folder warn user
         int choice = NSRunAlertPanel(NSLocalizedString(@"Warning", nil), [NSString stringWithFormat:NSLocalizedString(@"ImportNotInProjectFolder", nil), [self projectFolder]], NSLocalizedString(@"No", nil), NSLocalizedString(@"Yes", nil), nil);
         if(choice == NSAlertDefaultReturn) {
            //user wants to cancel import
            return;
         }   
      }

      while (path = [enumerator nextObject]) {
         if([path hasSuffix:@".mes"]) {
            //we are imprting a .mes chart
            NSFileWrapper *wrapper = [[[NSFileWrapper alloc] initWithPath:path] autorelease];
            if([wrapper isDirectory]) {
               NSDictionary *fileDictionary = [wrapper fileWrappers]; //all files in the loaded directory
               BOOL success = NO;
               [missingFiles removeAllObjects]; //we only want to display missing files for imported objects
               [actorOrder removeAllObjects];

               //get the structure dict for this file
               NSMutableDictionary *structure = [parser dictionaryWithStructureXMLData:[[fileDictionary objectForKey:@"_structure.xml"] regularFileContents]
                                                                              fileName:[wrapper filename]
                                                                       actorOrderArray:&actorOrder
                                                                            successful:&success];

               //if the import is adds more EBs than allowed cancel it and alert user
               if(![(SSCatalystAppController *)[NSApp delegate] isRegistered] && ([[self eventArray] count] + [[structure objectForKey:@"referencedEventBlocks"] count]) > 20) {
                  //if the user is not registered then limit the number of EBs to 20
                  NSRunAlertPanel(NSLocalizedString(@"UnableToPerformAction", nil), NSLocalizedString(@"EvaluationEBLimitExceeded", nil), @"OK", nil, nil, nil);
                  success = NO;
               }
               
               if(success) {
                  //the _structure file was valid
                  //add this new structure dict to the top level structure dict for the document
                  [[[documentData structureDictionary] objectForKey:@"subDirectories"] addObject:structure];

                  [self addFilesFromDictionary:fileDictionary
                                 structureDict:structure
                                 appendAddress:[NSString stringWithFormat:@"%@/", [wrapper filename]]];

                  [documentData convertLinksForEventsInDictionary:structure
                                  appendToFrontOfLink:[NSString stringWithFormat:@"%@/", [wrapper filename]]]; //convert the links for the newly added EBs
                  [documentData convertAllDiamondOwnersInDictionary:structure appendToFrontOfOwnerAddress:[NSString stringWithFormat:@"%@/", [wrapper filename]]];
                  [documentData convertAllCommentClosestEventsInDictionary:structure];

                  //add all of the ebs/comments to the main chart view
                  NSEnumerator *enumerator = [[documentData allEventsInStructure:structure] objectEnumerator];
                  id anObject;

                  while (anObject = [enumerator nextObject]) {
                     [self addEventToChartView:anObject];
                  }

                  enumerator = [[documentData allCommentsInStructure:structure] objectEnumerator];
                  while (anObject = [enumerator nextObject]) {
                     [self addCommentToChartView:anObject];
                  }

                  if([missingFiles count] != 0) {
                     //there are missing or invalid EBs
                     NSEnumerator *enumerator = [missingFiles objectEnumerator];
                     id anObject;
                     NSString *missingFilesString = [NSString string];

                     while (anObject = [enumerator nextObject]) {
                        //build a string with all missing files
                        missingFilesString = [missingFilesString stringByAppendingString:[NSString stringWithFormat:@"%@\n", anObject]];
                     }
                     //put up alert with missing file names
                     NSRunAlertPanel(NSLocalizedString(@"Error", nil), [NSString stringWithFormat:@"%@\n%@", NSLocalizedString(@"MissingEB", nil), missingFilesString], @"OK", nil, nil);
                  }
                  [documentData orderActorsForImportedFileBasedOnArray:actorOrder];

                  if([documentData importCanceled]) {
                     //the user has canceled the import so we need to delete all of the imported files
                     [documentData removeAllTracesOfStructureDict:structure];
                     [documentData setImportCanceled:NO];
                  } else {
                     //user hasn't canceled but they may want to skip events
                     NSEnumerator *enumerator = [skippedImportedEvents objectEnumerator];
                     id anObject;

                     while (anObject = [enumerator nextObject]) {
                        [self deleteObject:anObject];
                     }
                     //remove all of the events in skippedImportedEvents
                     [skippedImportedEvents removeAllObjects];
                     [chartView recalculateEBPositions];
                  }
               }

               if([self displayMode] == SSMatrixDisplayMode) {
                  [chartView sizeToFit];
               }
               
               [self reload];
            }
         } else {
            if([path hasSuffix:@".xml"]) {
               //importing an event
               BOOL success = NO;
               NSFileWrapper *wrapper = [[[NSFileWrapper alloc] initWithPath:path] autorelease];
               SSEventObject *eventObject = [parser eventObjectWithEventXMLData:[wrapper regularFileContents]
                                                                        address:[documentData addressForNewEvent]
                                                                          label:[NSNumber numberWithInt:SSWhiteLabelType]
                                                                      xPosition:[NSNumber numberWithFloat:NSMinX([chartView visibleRect])]
                                                                     successful:&success];
               [documentData convertLinksForEvent:eventObject];

               if(success) {
                  //we created a valid event
                  [self addEventToDocument:eventObject];
                  [documentData validateNewEventBeginTime:eventObject];

                  if([documentData importCanceled]) {
                     //the user has canceled the import so we need to delete all of the imported files
                     [self deleteObject:eventObject];
                     [documentData setImportCanceled:NO];
                  } else {
                     //user hasn't canceled but they may want to skip events
                     NSEnumerator *enumerator = [skippedImportedEvents objectEnumerator];
                     id anObject;

                     while (anObject = [enumerator nextObject]) {
                        [self deleteObject:anObject];
                     }
                     //remove all of the events in skippedImportedEvents
                     [skippedImportedEvents removeAllObjects];
                  }
               } else {
                  NSRunAlertPanel(NSLocalizedString(@"Error", nil), NSLocalizedString(@"ImportInvalidEventFile", nil), @"OK", nil, nil);
               }
            }
         }
      }
      [aManager endUndoGrouping];
   }
   [parser release];
}

- (void)addSkippedEvent:(SSEventObject *)event
{
   [skippedImportedEvents addObject:event];
}

//***********************************

- (void)windowControllerDidLoadNib:(NSWindowController *)aController
{
   NSEnumerator *eventEnumerator = [[documentData eventArray] objectEnumerator];
   id eventObject;
   NSEnumerator *commentEnumerator = [[documentData commentArray] objectEnumerator];
   id commentObject;

   NSWindow *window = [aController window];
   NSRect newFrame = [window frame];

   [super windowControllerDidLoadNib:aController];

   if(catalystDocument) {
      [self setFileType:@"Catalyst Document"];
   }

   [self setupToolbar]; //set our toolbar

   //grow the window to fit the screen
   newFrame.origin.x = [[window screen] visibleFrame].origin.x;
   newFrame.origin.y = [[window screen] visibleFrame].size.height;
   newFrame.size.width = [[window screen] visibleFrame].size.width;
   newFrame.size.height = [[window screen] visibleFrame].size.height;
   [window setFrame:newFrame display:YES];

   //set the display mode
   [self setDisplayMode:[documentData displayMode]];

   //set the overlap units
   if([documentData overlapUnit] != 0.0) {
      [self setOverlapUnit:[documentData overlapUnit]];
   }

   if([documentData overlapPixelsPerUnit] != 0) {
      [self setOverlapPixelsPerUnit:[documentData overlapPixelsPerUnit]];
   }
   

   [self setTableViewRowHeight]; //set the row heights
   [[[[tableView tableColumns] objectAtIndex:0] dataCell] setWraps:YES];
   [[[[tableView tableColumns] objectAtIndex:0] dataCell] setFont:[NSFont fontWithName:@"Helvetica" size:10]];

   //set  the row width
   float width = [documentData actorColumnWidth];
   float dif = width - NSWidth([[tableView enclosingScrollView] frame]);
   [[tableView enclosingScrollView] setFrameSize:NSMakeSize([documentData actorColumnWidth], NSHeight([[tableView enclosingScrollView] frame]))];
   [chartView setFrameSize:NSMakeSize((NSWidth([chartView frame]) - dif), NSHeight([chartView frame]))];
   [splitView adjustSubviews];
   
   //set the display for legend view
   [self setLegendViewDisplay];

   //create the jump map for this chart and add it to the view if needed
   jumpMapView = [[SSJumpMapView alloc] initWithFrame:NSMakeRect(0, 0, SSJumpMapSize.width, SSJumpMapSize.height) representedView:chartView];
   if([[NSUserDefaults standardUserDefaults] integerForKey:SSDisplayJumpMapKey] == preferenceJumpMapInChartTag) {
      [self addJumpMapToChartView];
   }

   while (eventObject = [eventEnumerator nextObject]) {
      //enumerate through the events and add their views to the chart
      [chartView addEventView:[eventObject eventView]];
   }

   while (commentObject = [commentEnumerator nextObject]) {
      //enumerate through the comments and add their views to the chart
      [chartView addCommentView:[commentObject commentView]];
   }

   [[documentData eventArray] makeObjectsPerformSelector:@selector(updateLinks)];

   if([self displayMode] == SSMatrixDisplayMode) {
      [chartView sizeToFit];
   } else {
      [chartView sizeToEndDate:[self overlapViewToDate]];
   }
   
   [chartView scrollRectToVisible:NSMakeRect(0, 0, 1, 1)];
}

- (void)switchDisplayMode
{
   if([chartView displayMode] == SSMatrixDisplayMode) {
      int result;

      //ask for scale and date range
      result = [SSOverlapSwitchScaleAndRangeController displayOverlapSwitchScaleAndRangePanel];

      if(result == SSActionApproved) {
         //user has asked to display the overlap
         [self setDisplayMode:SSOverlapDisplayMode];

         [self promptUserOnOutOfRangeEBs];

         //remove the comments
         [[chartView commentViews] makeObjectsPerformSelector:@selector(removeFromSuperview)];
      }
   } else {
      [self setDisplayMode:SSMatrixDisplayMode];

      //add the comments
      NSEnumerator *enumerator = [[self commentArray] objectEnumerator];
      id anObject;

      while (anObject = [enumerator nextObject]) {
      	if([anObject isVisible]) {
           [chartView addCommentView:[anObject commentView]];
      	}
      }
   }
}

- (void)addFilesFromDictionary:(NSDictionary *)fileDictionary structureDict:(NSMutableDictionary *)structureDict appendAddress:(NSString *)appendToFront
{
   //gets all referenced files in events from fileDictionary and appends appendToFront to the front of their address variable
   NSEnumerator *directoryEnumerator = [fileDictionary keyEnumerator];
   id key;
   NSEnumerator *eventsEnumerator = [[structureDict objectForKey:@"referencedEventBlocks"] objectEnumerator];
   id referencedEvent;
   NSMutableArray *events = [NSMutableArray array];

   NSString *addressString;
   SSXMLParser *parser = [[SSXMLParser alloc] init];

   NSMutableArray *diamonds;

   //add the comments
   [documentData addCommentsFromArray:[structureDict objectForKey:@"comments"]];
      
   while (referencedEvent = [eventsEnumerator nextObject]) {
      //loop through events
      addressString = [referencedEvent objectForKey:@"address"];
      NSFileWrapper *eventFile = [fileDictionary objectForKey:addressString]; //pull referencedEvent from fileDictionary

      if(eventFile) {
         //we found a file by the referenced name so try and load it
         BOOL success;
         addressString = [appendToFront stringByAppendingString:[referencedEvent objectForKey:@"address"]];
         SSEventObject *eventObject = [parser eventObjectWithEventXMLData:[eventFile regularFileContents] address:addressString
                                                                    label:[referencedEvent objectForKey:@"label"]
                                                                xPosition:[referencedEvent objectForKey:@"xPosition"]
                                                               successful:&success];
         if(success) {
            //add this object to the events array
            [events addObject:eventObject];
         } else {
            //couldn't load add this file to list of errors and create a ghost object
            SSEventObject *eventObject = [[SSEventObject alloc] initAsGhostEventWithAddress:addressString
                                                                       label:[referencedEvent objectForKey:@"label"]
                                                                   xPosition:[[referencedEvent objectForKey:@"xPosition"] floatValue]];
            [missingFiles addObject:addressString];

            //add this object to the events array
            [events addObject:eventObject];
         }
      } else {
         //no file by that name, add to files which aren't present and create a ghost object
         SSEventObject *eventObject = [[SSEventObject alloc] initAsGhostEventWithAddress:addressString
                                                                    label:[referencedEvent objectForKey:@"label"]
                                                                xPosition:[[referencedEvent objectForKey:@"xPosition"] floatValue]];
         [missingFiles addObject:addressString];

         //add this object to the events array
         [events addObject:eventObject];
      }
   }

   //replace the referencedEvents array in structureDict with the array of actual events
   [structureDict setObject:events forKey:@"referencedEventBlocks"];

   //add events to the other parts of the chart
   [documentData addEventsFromArray:events];

   //load the diamond file
   if([fileDictionary objectForKey:@"diamond.xml"]) {
      diamonds = [parser diamondArrayFromXMLData:[[fileDictionary objectForKey:@"diamond.xml"] regularFileContents]];
      [structureDict setObject:diamonds forKey:@"needsDiamonds"];
      [documentData addNeedsDiamondsFromArray:diamonds];
   }

   //loop through file directory looking for nested .mes files
   while (key = [directoryEnumerator nextObject]) {
      NSFileWrapper *wrapper = [fileDictionary objectForKey:key];
      if([wrapper isDirectory]) {
         //it's a directory lets see if it is a .mes directory
         if([self isValidMESFileName:key]) {
            //it is
            //see if it has a _structure.xml file
            NSDictionary *files = [wrapper fileWrappers];
            if([files objectForKey:@"_structure.xml"]) {
               //it has a _structure.xml
               BOOL success;
               
               //get the structure dict for this file
               NSMutableDictionary *structure = [parser dictionaryWithStructureXMLData:[[files objectForKey:@"_structure.xml"] regularFileContents]
                                                                              fileName:[wrapper filename]
                                                                       actorOrderArray:&actorOrder
                                                                            successful:&success];

               if(success) {
                  //the _structure file was valid
                  //add this new structure dict to the subDirectories array of the last structure dict
                  [[structureDict objectForKey:@"subDirectories"] addObject:structure];
                     
                  NSString *append = [appendToFront stringByAppendingString:[NSString stringWithFormat:@"%@/", key]];

                  //run this method again but for the new structure dict
                  [self addFilesFromDictionary:files
                                  structureDict:structure
                                  appendAddress:append];
               }
            }
         }
      }
   }

   [parser release];
}

//***********************************creating and deleting
- (void)addNewEventBlock:(SSEventObject *)event
{
   [self addEventToDocument:event];

   if([self displayMode] == SSMatrixDisplayMode) {
      //move the event view to the 0 X of the charts visible rect and scroll down
      [[event eventView] setNewOriginWithPoint:NSMakePoint(NSMinX([chartView visibleRect]), NSMinY([[event eventView] frame]))];
      [chartView sizeToFit];
   }
   
   [chartView scrollRectToVisible:[[event eventView] frame]];
}

- (void)newAlertComment
{
   [self addCommentOfType:[NSNumber numberWithInt:SSCommentAlertType]];
}

- (void)newNoteComment
{
   [self addCommentOfType:[NSNumber numberWithInt:SSCommentNoteType]];
}

- (void)newQuestionComment
{
   [self addCommentOfType:[NSNumber numberWithInt:SSCommentQuestionType]];
}

- (void)addCommentOfType:(NSNumber *)type
{
   SSCatalystComment *newComment = [[[SSCatalystComment alloc] initWithUnique_id:[[SSChartDate calendarDate] descriptionWithCalendarFormat:@"%y%m%d%H%M%F"]
                                                                         comment:@"Insert comment here"
                                                                            type:type
                                                                        position:NSMakePoint(NSMinX([chartView visibleRect]),
                                                                                             (NSMinY([chartView visibleRect]) + (NSHeight([chartView visibleRect]) / 2)))] autorelease];
   [documentData addCommentToDocument:newComment structureDictionary:nil];
   [chartView addCommentView:[newComment commentView]];
   [[newComment commentView] selectFieldForEditing];
   [newComment setClosestEvent];
}

- (void)addCommentToDocument:(SSCatalystComment *)comment
{
   [documentData addCommentToDocument:comment structureDictionary:nil];   
}

- (BOOL)canAddNeedsDiamond
{
   if([self displayMode] != SSMatrixDisplayMode) {
      return NO;
   } else if([[self selectedObjects] count] != 1) {
      return NO; //we have more than one object selected or no objects selected
   } else { //we have only 1 object selected lets see if it is of a class that can have a diamond attached
      if(![[[self selectedObjects] objectAtIndex:0] isKindOfClass:[SSEventView class]] && ![[[self selectedObjects] objectAtIndex:0] isKindOfClass:[SSEventLinkObject class]]) {
         //the selected object cant have a diamond
         return NO;
      }
   }

   return YES;
}

- (void)addNeedsDiamondToSelectedObject
{
   if([[self selectedObjects] count] == 1) {
      if([[[self selectedObjects] objectAtIndex:0] isKindOfClass:[SSEventView class]] || [[[self selectedObjects] objectAtIndex:0] isKindOfClass:[SSEventLinkObject class]]) {
         [self addNeedsDiamondToObject:[[self selectedObjects] objectAtIndex:0]];
      }
   }
}

- (void)addNeedsDiamondToObject:(id)diamondOwner
{   
   if([diamondOwner isKindOfClass:[SSEventView class]]) {
      //it's an event view so we need to use its owner rather than the selected object itself
      diamondOwner = [diamondOwner owner];
   }

   SSNeedsDiamond *newDiamond = [[[SSNeedsDiamond alloc] initWithOwner:diamondOwner
                                                                fileID:[documentData defaultNeedsDiamondValueForKey:@"fileID"]
                                                               analyst:[documentData defaultNeedsDiamondValueForKey:@"analyst"]
                                                           description:[NSString string]
                                                           optionArray:[NSMutableArray array]
                                                          diamondState:[NSNumber numberWithInt:SSNeedsDiamondDraftState]] autorelease];
   
   //add the diamond to the diamond owner
   [diamondOwner addNeedsDiamond:newDiamond];

   //get the structure file that this diamond needs to be added to
   NSMutableDictionary *structure;
   if([diamondOwner isKindOfClass:[SSEventObject class]]) {
      structure = [documentData structureDictionaryForObject:diamondOwner];
   } else {
      //it's a link so we need to add it to the structure dict for linked from
      structure = [documentData structureDictionaryForObject:[diamondOwner linkedFrom]];
   }

   //add the diamond to the chart
   [documentData addNeedsDiamondToDocument:newDiamond structureDictionary:structure];

   //select the new diamond and display the inspector
   [chartView selectAndDisplayObject:newDiamond];
   [self showInspectorPanel];
}

- (IBAction)deleteSelectedObjects:(id)sender
{
   [self deleteSelectedObjects];
   [chartView setNeedsDisplayInRect:[chartView visibleRect]];
}

- (void)deleteSelectedObjects
{
   NSEnumerator *enumerator = [[chartView selectedObjects] objectEnumerator]; //get an array with all selected views
   id anObject;
   
   while (anObject = [enumerator nextObject]) {
      //enumerate through the selected objects and remove them from the chart
      if([anObject isKindOfClass:[SSEventView class]] || [anObject isKindOfClass:[SSCommentView class]]) {
         [self deleteObject:[anObject owner]];
      } else {
         [self deleteObject:anObject];
      }
   }

   [chartView invalidateSelection];
}

- (void)deleteObject:(id)anObject
{
   //any object being deleted from the chat should be sent through the method, if it is not there is a good chance that all references will not be removed from the chart
   NSUndoManager* aManager = [self undoManager];
   NSEnumerator *enumerator;
   id linkObject;
   NSEnumerator *diamondEnumerator;
   id diamondObject;

   if([anObject isKindOfClass:[NSMenuItem class]]) {
      //in case a delete message is sent from a menu
      anObject = [anObject representedObject];
   }

   if([anObject isKindOfClass:[SSEventObject class]]) {
      //it's an EB
      NSString *actorName = [anObject actor];

      [aManager beginUndoGrouping];
      [[aManager prepareWithInvocationTarget:documentData] addEventToDocument:anObject structureDictionary:[documentData structureDictionaryForObject:anObject]];

      //enumerate through the diamonds and register for undo
      diamondEnumerator = [[anObject allRelatedNeedsDiamonds] objectEnumerator];
      while (diamondObject = [diamondEnumerator nextObject]) {
         [[aManager prepareWithInvocationTarget:[diamondObject owner]] addNeedsDiamond:diamondObject];
         if([[diamondObject owner] isKindOfClass:[SSEventObject class]]) {
            [[aManager prepareWithInvocationTarget:documentData] addNeedsDiamondToDocument:diamondObject structureDictionary:[documentData structureDictionaryForObject:[diamondObject owner]]];
         } else {
            //else it is a link and we need to get the structureDictionary for its linkedFrom object
            [[aManager prepareWithInvocationTarget:documentData] addNeedsDiamondToDocument:diamondObject structureDictionary:[documentData structureDictionaryForObject:[[diamondObject owner] linkedFrom]]];
         }
      }
      
      //enumerate through the EBs links and set the action so that they will re link on undo
      enumerator = [[anObject linkArray] objectEnumerator];
      while (linkObject = [enumerator nextObject]) {
         [aManager registerUndoWithTarget:anObject selector:@selector(addLink:) object:linkObject];
      }

      //enumerate through the EBs links from and set the action so that they will re link on undo
      enumerator = [[anObject linkFromArray] objectEnumerator];
      while (linkObject = [enumerator nextObject]) {
         [aManager registerUndoWithTarget:linkObject selector:@selector(addLink:) object:[linkObject linkObjectConnectingEvent:anObject]];
      }
      [aManager endUndoGrouping];

      [[anObject eventView] removeFromSuperview];
      [documentData removeEventFromDocument:anObject];

      if([self numberOfActorsWithName:actorName] == 0) {
         //this row was removed so we need the others to update
         [chartView recalculateEBPositions];
      }
   } else if([anObject isKindOfClass:[SSCatalystComment class]]) {
      //it's a comment
      [[aManager prepareWithInvocationTarget:documentData] addCommentToDocument:anObject structureDictionary:[documentData structureDictionaryForObject:anObject]];
      [[anObject commentView] removeFromSuperview];
      [documentData removeCommentFromDocument:anObject];
   } else if([anObject isKindOfClass:[SSNeedsDiamond class]]) {
      //it's a diamond
      [aManager beginUndoGrouping];
      [[aManager prepareWithInvocationTarget:[anObject owner]] addNeedsDiamond:anObject];
      if([[anObject owner] isKindOfClass:[SSEventObject class]]) {
         [[aManager prepareWithInvocationTarget:documentData] addNeedsDiamondToDocument:anObject structureDictionary:[documentData structureDictionaryForObject:[anObject owner]]];
      } else {
         //else it is a link and we need to get the structureDictionary for its linkedFrom object
         [[aManager prepareWithInvocationTarget:documentData] addNeedsDiamondToDocument:anObject structureDictionary:[documentData structureDictionaryForObject:[[anObject owner] linkedFrom]]];
      }
      [aManager endUndoGrouping];
         
      [documentData removeNeedsDiamondFromDocument:anObject];
   } else if([anObject isKindOfClass:[SSEventLinkObject class]]) {
      //it's a link
      //make sure that there are linked to and linked from objects, if there is not this link is being removed by another process and deleting it here would crash the app
      if([anObject linkedFrom] && [anObject linkedTo]) {
         //remove all of the links needs diamonds
         [aManager beginUndoGrouping];
         NSEnumerator *diamondEnumerator = [[anObject needsDiamondArray] objectEnumerator];
         id diamondObject;
         while (diamondObject = [diamondEnumerator nextObject]) {
            [self deleteObject:diamondObject];
         }

         [aManager registerUndoWithTarget:[anObject linkedFrom] selector:@selector(addLink:) object:anObject];
         
         [[anObject linkedFrom] removeLinkTo:[anObject linkedTo]]; //get the Event to remove the selected link
         [aManager endUndoGrouping];
      }
   }

   [self reload];
}

//***********************************
- (void)addEventToChartView:(SSEventObject *)event
{
   //add events eventView to the chart
   [chartView addEventView:[event eventView]];   
}

- (void)addCommentToChartView:(SSCatalystComment *)com
{
   [chartView addCommentView:[com commentView]];
}

- (void)updateObjectVisibility
{
   NSEnumerator *eventEnumerator = [[documentData eventArray] objectEnumerator];
   SSEventObject *eventObject;
   NSEnumerator *commentEnumerator = [[documentData commentArray] objectEnumerator];
   SSCatalystComment *commentObject;

   if([self displayMode] == SSOverlapDisplayMode) {
      //in overlap display so change the display date
      [chartView setOverlapViewFromDate:[self overlapViewFromDate]];
      [chartView sizeToEndDate:[self overlapViewToDate]];
   }
   
//   [[documentData commentArray] makeObjectsPerformSelector:@selector(setClosestEvent)]; //get the comments to set the nearest event
                                                                                       //we will use this to set their visibility
   
   while (eventObject = [eventEnumerator nextObject]) {
      if(![objectFilterController passesFilter:eventObject]) {
         [[eventObject eventView] removeFromSuperview];
         [eventObject setIsVisible:NO];
      } else {
         //it passed
         if(![eventObject isVisible]) { //if it is already visible we dont want to add it to the view
            [eventObject setIsVisible:YES];
            [chartView addEventView:[eventObject eventView]];
         }
      }
   }

   while (commentObject = [commentEnumerator nextObject]) {
      if(![[commentObject closestEvent] isVisible]) {
         //our closestEvent is not visible
         if([self displayMode] == SSMatrixDisplayMode) {
            [[commentObject commentView] removeFromSuperview];
         }
         
         [commentObject setIsVisible:NO];
      } else {
         //our closestEvent is visible so we need to be also
         if(![commentObject isVisible]) { //if it is already visible we dont want to add it to the view
            [commentObject setIsVisible:YES];
            if([self displayMode] == SSMatrixDisplayMode) {
               [chartView addCommentView:[commentObject commentView]];
            }
         }
      }
   }

   [documentData updateFilteredActorArray];

   [tableView reloadData];

   [chartView recalculateEBPositions];
}

- (void)updateVisibilityOfEvent:(SSEventObject *)event
{
   if(![objectFilterController passesFilter:event]) {
      if([event isVisible]) { //if it's not visible already do nothing
         [[event eventView] removeFromSuperview];
         [event setIsVisible:NO];
         
         [documentData updateFilteredActorArray];
         [tableView reloadData];
         [chartView recalculateEBPositions];
      }
   } else {
      //it passed
      if(![event isVisible]) { //if it is already visible we dont want to add it to the view
         [event setIsVisible:YES];
         [chartView addEventView:[event eventView]];

         [documentData updateFilteredActorArray];
         [tableView reloadData];
         [chartView recalculateEBPositions];
      }
   }
}

//***********************************

- (void)promptUserOnOutOfRangeEBs
{
   //called when user switches to overlap. if an eb starts after a filter start date
   //and finishes after a filter start date gives user option to change EB or override filter altogether

   if([objectFilterController filterWithIdentifier:SSStartsAfterDateFilterType] &&
      [objectFilterController filterWithIdentifier:SSEndsBeforeDateFilterType]) {
      //both filters are active so we are going to prompt users
      NSMutableArray *eventsForPrompt = [NSMutableArray array];
      int i = 0;
      int choice;

      while(i < [[self eventArray] count]) {
         if(![[[self eventArray] objectAtIndex:i] isVisible]) {
      	   //this event has been filtered, see if it requires a prompt
            SSEventObject *event = [[self eventArray] objectAtIndex:i];
            if([[event beginTime] compare:[[objectFilterController filterWithIdentifier:SSStartsAfterDateFilterType] filterValue]] == NSOrderedDescending) {
               //it starts after the begin filter, see if it starts before the end filter
               if([[event beginTime] compare:[[objectFilterController filterWithIdentifier:SSEndsBeforeDateFilterType] filterValue]] == NSOrderedAscending) {
                  //it also starts before the end of the filter range so we want to promp the user
                  [eventsForPrompt addObject:event];
               }
            }
      	}
         i++;
      }

      i = 0;
      while(i < [eventsForPrompt count]) {
         //loop through the events for prompt array and prompt for each
         choice = NSRunAlertPanel(NSLocalizedString(@"Alert", nil), [NSString stringWithFormat:@"%@ %@ - %@ %@",
            NSLocalizedString(@"PartlyWithinFilterRangeP1", nil),
            [(SSEventObject *)[eventsForPrompt objectAtIndex:i] actor],
            [(SSEventObject *)[eventsForPrompt objectAtIndex:i] action],
            NSLocalizedString(@"PartlyWithinFilterRangeP2", nil)],
                                  NSLocalizedString(@"ExcludeEB", nil), NSLocalizedString(@"EditEB", nil), NSLocalizedString(@"DisplayEB", nil), nil);

         switch(choice) {
            case NSAlertAlternateReturn:
               //user wants to edit
               [[SSEBDuplicateEditPanelController sharedEBDuplicateEditPanelController] showWindowForEvent:[eventsForPrompt objectAtIndex:i]];
               //update this events visibility
               [self updateVisibilityOfEvent:[eventsForPrompt objectAtIndex:i]];
               break;
            case NSAlertOtherReturn:
               //user wants to display override the filter
               [[eventsForPrompt objectAtIndex:i] setIsVisible:YES];

               if(![[self filteredActorArray] containsObject:[[eventsForPrompt objectAtIndex:i] actor]]) {
                  //there is no row for this event so we need to add one
                  [documentData updateFilteredActorArray];
                  [tableView reloadData];
                  [chartView recalculateEBPositions];
               }
                  [chartView addEventView:[[eventsForPrompt objectAtIndex:i] eventView]];
                  break;
            default:
               break;
         }
         i++;
      }
   }
}

//***********************************

- (SSChartDate *)overlapViewFromDate
{
   if([objectFilterController filterWithIdentifier:SSStartsAfterDateFilterType]) {
      //there is a filter so return that date
      return [[objectFilterController filterWithIdentifier:SSStartsAfterDateFilterType] filterValue];
   } else {
     // no start filter return the first start date
      return [self earliestEBBeginTime];
   }
}

- (SSChartDate *)overlapViewToDate
{
   if([objectFilterController filterWithIdentifier:SSEndsBeforeDateFilterType]) {
      //there is a filter so return that date
      return [[objectFilterController filterWithIdentifier:SSEndsBeforeDateFilterType] filterValue];
   } else {
      // no start filter return the first start date
      return [self latestEBEndTime];
   }
}


- (SSChartDate *)earliestEBBeginTime
{
   NSArray *sortedEvents = [[documentData eventArray] sortedArrayUsingSelector:@selector(compareByBeginTime:)];
   if([sortedEvents count] > 0) {
      return [[sortedEvents objectAtIndex:0] beginTime];
   } else {
      return [SSChartDate calendarDate];
   }
}

- (SSChartDate *)latestEBEndTime
{
   NSArray *sortedEvents = [[documentData eventArray] sortedArrayUsingSelector:@selector(compareByEndTime:)];
   if([sortedEvents count] > 0) {
      return [[sortedEvents lastObject] endTime];
   } else {
      return [SSChartDate calendarDate];
   }
}

//***********************************

- (BOOL)isValidMESFileName:(NSString *)string
{
   //return YES is string is a .mes file name NO otherwise
   NSRange range = [string rangeOfString:@".mes" options:NSCaseInsensitiveSearch range:NSMakeRange(([string length] - 4), 4)];

   if(range.location != NSNotFound) {
      //it has a .mes extension
      return YES;
   }

   return NO; //not the file we are after
}


//***********************************

- (NSMutableArray *)selectedObjects
{
   return [chartView selectedObjects];
}

- (SSEventObject *)firstSelectedEB
{
   NSMutableArray *array = [chartView selectedObjects];
   int i = 0;
   SSEventObject *firstSelectedEvent = nil;
   
   while(i < [array count]) {
      if([[[array objectAtIndex:i] owner] isKindOfClass:[SSEventObject class]]) {
         //it's an event object so it is a canditate
         if(firstSelectedEvent) {
            if([[[array objectAtIndex:i] owner] xPosition] < [firstSelectedEvent xPosition]) {
               //this is before the current first selected of change it
               firstSelectedEvent = [[array objectAtIndex:i] owner];
            }
         } else {
            //there is no firstSelectedEvent set so use this one
            firstSelectedEvent = [[array objectAtIndex:i] owner];
         }
      }
      i++;
   }

   return firstSelectedEvent;
}

- (void)selectAndDisplayObject:(id)object
{
   [chartView selectAndDisplayObject:object];
}

- (IBAction)stepEBBack:(id)sender
{
   SSEventObject *startingEvent = [self firstSelectedEB];
   NSArray *sortedEvents = [[documentData eventArray] sortedArrayUsingSelector:@selector(compareByXPosition:)];

   if([sortedEvents count] > 0) {
      if(!startingEvent) {
         //no event selected so select and display the first event
         [chartView selectAndDisplayObject:[[sortedEvents objectAtIndex:0] eventView]];
      } else {
         if([sortedEvents indexOfObject:startingEvent] > 0) {
            //display the object before starting event
            [chartView selectAndDisplayObject:[[sortedEvents objectAtIndex:([sortedEvents indexOfObject:startingEvent] - 1)] eventView]];
         } else {
            //starting event is the first object so move to the last object
            [chartView selectAndDisplayObject:[[sortedEvents lastObject] eventView]];
         }
      }
   }
}

- (IBAction)stepEBForward:(id)sender
{
   SSEventObject *startingEvent = [self firstSelectedEB];
   NSArray *sortedEvents = [[documentData eventArray] sortedArrayUsingSelector:@selector(compareByXPosition:)];

   if([sortedEvents count] > 0) {
      if(!startingEvent) {
         //no event selected so select and display the last event
         [chartView selectAndDisplayObject:[[sortedEvents lastObject] eventView]];
      } else {
         if(![startingEvent isEqualTo:[sortedEvents lastObject]]) {
            //display the object after starting event
            [chartView selectAndDisplayObject:[[sortedEvents objectAtIndex:([sortedEvents indexOfObject:startingEvent] + 1)] eventView]];
         } else {
            //starting event is the last object so move to the first object
            [chartView selectAndDisplayObject:[[sortedEvents objectAtIndex:0] eventView]];
         }
      }
   }
}


//***********************************
- (void)setAllSelectedEventsWithLabel:(NSNumber *)label
{
   NSEnumerator *enumerator = [[self selectedObjects] objectEnumerator];
   id anObject;

   //loop through the selected objects if an EB is found set it's label
   while (anObject = [enumerator nextObject]) {
      if([anObject isKindOfClass:[SSEventView class]]) {
         [(SSEventObject *)[anObject owner] setLabel:label];
      }
   }
}

//***********************************documentData accessor methods
- (void)addEventToDocument:(SSEventObject *)event
{
   [documentData addEventToDocument:event structureDictionary:nil];
}

- (void)addEventToAalosDictionary:(SSEventObject *)event
{
   [documentData addEventToAalosDictionary:event];
}

- (NSString *)dataSourceDictionaryArrayWithKey:(NSString *)key objectAtIndex:(int)index
{
   return [documentData dataSourceDictionaryArrayWithKey:key objectAtIndex:index];
}

- (NSString *)alphabeticDataSourceDictionaryArrayWithKey:(NSString *)key objectAtIndex:(int)index
{
   return [documentData alphabeticDataSourceDictionaryArrayWithKey:key objectAtIndex:index];
}

- (int)dataSourceDictionaryIndexOfObject:(NSString *)object inArray:(NSString *)arrayName
{
   return [documentData dataSourceDictionaryIndexOfObject:object inArray:arrayName];
}

- (NSString *)addressForNewEvent
{
   return [documentData addressForNewEvent];   
}

- (SSEventObject *)eventWithAddress:(NSString *)address
{
   return [documentData eventWithAddress:address];
}

- (NSMutableArray *)filteredActorArray
{
   return [documentData filteredActorArray];
}

- (int)numberOfUniqueSources
{
   return [documentData numberOfUniqueSources];
}

- (int)numberOfUniqueActors
{
   return [documentData numberOfUniqueActors];
}

- (int)numberOfUniqueActions
{
   return [documentData numberOfUniqueActions];
}

- (int)numberOfUniqueLocations
{
   return [documentData numberOfUniqueLocations];
}

- (int)numberOfUniqueObjects
{
   return [documentData numberOfUniqueObjects];
}

- (int)numberOfUniqueRemarks
{
   return [documentData numberOfUniqueRemarks];
}

- (int)numberOfActorsWithName:(NSString *)name
{
   return [documentData numberOfActorsWithName:name];
}

- (NSMutableArray *)commentArray
{
   return [documentData commentArray];
}

- (NSMutableArray *)eventArray
{
   return [documentData eventArray];
}

- (NSMutableArray *)needsDiamondArray
{
   return [documentData needsDiamondArray];
}

- (void)validateEventBeginTime:(SSEventObject *)event
{
   [documentData validateEventBeginTime:event];
}

- (BOOL)eventExistsWithActorAndTime:(SSEventObject *)event
{
   return [documentData eventExistsWithActorAndTime:event];
}

- (NSString *)title
{
   return [documentData title];
}

- (void)setTitle:(NSString *)string
{
   [documentData setTitle:string];
}

- (NSString *)author
{
   return [documentData author];
}

- (void)setAuthor:(NSString *)string
{
   [documentData setAuthor:string];
}

- (NSCalendarDate *)chartDate
{
   return [documentData chartDate];
}

- (void)setChartDate:(NSCalendarDate *)date
{
   [documentData setChartDate:date];
}

- (NSString *)whiteColorDesignation
{
   return [documentData whiteColorDesignation];
}

- (void)setWhiteColorDesignation:(NSString *)string
{
   [documentData setWhiteColorDesignation:string];
}

- (NSString *)yellowColorDesignation
{
   return [documentData yellowColorDesignation];
}

- (void)setYellowColorDesignation:(NSString *)string
{
   [documentData setYellowColorDesignation:string];
}

- (NSString *)greenColorDesignation
{
   return [documentData greenColorDesignation];
}

- (void)setGreenColorDesignation:(NSString *)string
{
   [documentData setGreenColorDesignation:string];
}

- (NSString *)lightBlueColorDesignation
{
   return [documentData lightBlueColorDesignation];
}

- (void)setLightBlueColorDesignation:(NSString *)string
{
   [documentData setLightBlueColorDesignation:string];
}

- (NSString *)blueColorDesignation
{
   return [documentData blueColorDesignation];
}

- (void)setBlueColorDesignation:(NSString *)string
{
   [documentData setBlueColorDesignation:string];
}

- (NSString *)purpleColorDesignation
{
   return [documentData purpleColorDesignation];
}

- (void)setPurpleColorDesignation:(NSString *)string
{
   [documentData setPurpleColorDesignation:string];
}

- (NSString *)orangeColorDesignation
{
   return [documentData orangeColorDesignation];
}

- (void)setOrangeColorDesignation:(NSString *)string
{
   [documentData setOrangeColorDesignation:string];
}

- (NSString *)redColorDesignation
{
   return [documentData redColorDesignation];
}

- (void)setRedColorDesignation:(NSString *)string
{
   [documentData setRedColorDesignation:string];
}

- (NSString *)diamondUDValue1
{
   return [documentData diamondUDValue1];
}

- (void)setDiamondUDValue1:(NSString *)string
{
   [documentData setDiamondUDValue1:string];
}

- (NSString *)diamondUDValue2
{
   return [documentData diamondUDValue2];
}

- (void)setDiamondUDValue2:(NSString *)string
{
   [documentData setDiamondUDValue2:string];
}

- (NSString *)diamondUDValue3
{
   return [documentData diamondUDValue3];
}

- (void)setDiamondUDValue3:(NSString *)string
{
   [documentData setDiamondUDValue3:string];
}

- (NSString *)diamondUDValue4
{
   return [documentData diamondUDValue4];
}

- (void)setDiamondUDValue4:(NSString *)string
{
   [documentData setDiamondUDValue4:string];
}

- (NSString *)diamondUDValue5
{
   return [documentData diamondUDValue5];
}

- (void)setDiamondUDValue5:(NSString *)string
{
   [documentData setDiamondUDValue5:string];
}

- (NSString *)diamondUDValue6
{
   return [documentData diamondUDValue6];
}

- (void)setDiamondUDValue6:(NSString *)string
{
   [documentData setDiamondUDValue6:string];
}

- (NSString *)diamondOptionUDValue1
{
   return [documentData diamondOptionUDValue1];
}

- (void)setDiamondOptionUDValue1:(NSString *)string
{
   [documentData setDiamondOptionUDValue1:string];
}

- (NSString *)diamondOptionUDValue2
{
   return [documentData diamondOptionUDValue2];
}

- (void)setDiamondOptionUDValue2:(NSString *)string
{
   [documentData setDiamondOptionUDValue2:string];
}

- (NSString *)diamondOptionUDValue3
{
   return [documentData diamondOptionUDValue3];
}

- (void)setDiamondOptionUDValue3:(NSString *)string
{
   [documentData setDiamondOptionUDValue3:string];
}

- (NSString *)diamondOptionUDValue4
{
   return [documentData diamondOptionUDValue4];
}

- (void)setDiamondOptionUDValue4:(NSString *)string
{
   [documentData setDiamondOptionUDValue4:string];
}

- (NSString *)diamondOptionUDValue5
{
   return [documentData diamondOptionUDValue5];
}

- (void)setDiamondOptionUDValue5:(NSString *)string
{
   [documentData setDiamondOptionUDValue5:string];
}

- (NSString *)diamondTradeoffUDValue1
{
   return [documentData diamondTradeoffUDValue1];
}

- (void)setDiamondTradeoffUDValue1:(NSString *)string
{
   [documentData setDiamondTradeoffUDValue1:string];
}

- (NSString *)diamondTradeoffUDValue2
{
   return [documentData diamondTradeoffUDValue2];
}

- (void)setDiamondTradeoffUDValue2:(NSString *)string
{
   [documentData setDiamondTradeoffUDValue2:string];
}

- (NSString *)diamondTradeoffUDValue3
{
   return [documentData diamondTradeoffUDValue3];
}

- (void)setDiamondTradeoffUDValue3:(NSString *)string
{
   [documentData setDiamondTradeoffUDValue3:string];
}

- (void)setDefaultNeedsDiamondValue:(NSString *)value forKey:(NSString *)key
{
   [documentData setDefaultNeedsDiamondValue:value forKey:key];   
}

- (NSString *)defaultNeedsDiamondValueForKey:(NSString *)key
{
   return [documentData defaultNeedsDiamondValueForKey:key];
}

- (NSString *)projectFolder
{
   return [documentData projectFolder];
}

- (void)setProjectFolder:(NSString *)path
{
   [documentData setProjectFolder:path];   
}

- (SSEventObject *)currentLinkFromEvent
{
   return [documentData currentLinkFromEvent];
}

- (void)setCurrentLinkFromEvent:(NSMenuItem *)item
{
   [documentData setCurrentLinkFromEvent:[item representedObject]];
}

- (SSEventObject *)closestEventToComment:(SSCatalystComment *)comment
{
   return [documentData closestEventToComment:comment];
}

- (NSMutableArray *)allEventsContainingString:(NSString *)string
{
   return [documentData allEventsContainingString:string];
}

- (NSMutableArray *)allEventsWithActorContainingString:(NSString *)string
{
   return [documentData allEventsWithActorContainingString:string];
}

- (NSMutableArray *)allEventsWithActionContainingString:(NSString *)string
{
   return [documentData allEventsWithActionContainingString:string];
}
- (NSMutableArray *)allEventsWithObjectContainingString:(NSString *)string
{
   return [documentData allEventsWithObjectContainingString:string];
}

- (NSMutableArray *)allEventsWithSourceContainingString:(NSString *)string
{
   return [documentData allEventsWithSourceContainingString:string];
}

- (NSMutableArray *)allEventsWithLocationContainingString:(NSString *)string
{
   return [documentData allEventsWithLocationContainingString:string];
}

- (NSMutableArray *)allEventsWithRemarkContainingString:(NSString *)string
{
   return [documentData allEventsWithRemarkContainingString:string];
}

- (NSMutableArray *)allEventsWithBeginDate:(NSCalendarDate *)date
{
   return [documentData allEventsWithBeginDate:date];
}

- (NSMutableArray *)allEventsWithBeginTime:(NSCalendarDate *)date
{
   return [documentData allEventsWithBeginTime:date];
}

- (NSMutableArray *)allEventsWithEndDate:(NSCalendarDate *)date
{
   return [documentData allEventsWithEndDate:date];
}

- (NSMutableArray *)allEventsWithEndTime:(NSCalendarDate *)date
{
   return [documentData allEventsWithEndTime:date];   
}

- (NSMutableArray *)allCommentsContainingString:(NSString *)string
{
   return [documentData allCommentsContainingString:string];
}

- (NSMutableArray *)allDiamondsContainingString:(NSString *)string
{
   return [documentData allDiamondsContainingString:string];
}
//***********************************

- (void)showNewEBPanel
{
   //display the new eb panel so user can add an event
   [[SSSharedNewEBPanelController sharedNewEBController] showWindow:self];
}

//***********************************printing and exporting code
- (BOOL)preparePageLayout:(NSPageLayout *)pageLayout
{
   [pageLayout setAccessoryView:[self printPanelAccessoryView]];

   return YES;
}

- printPanelAccessoryView
{
   if (printPanelAccessoryView == nil) {
      [NSBundle loadNibNamed:@"PrintPanelAccessoryView.nib" owner:self];
      [printPanelAccessoryView retain];
   }

   return printPanelAccessoryView;
}

- (IBAction)scalePrintToNumPages:(id)sender
{
   printPagesWide = [printPanelPagesWideField intValue];
   printPagesHigh = [printPanelPagesHighField intValue];
}

- (void)scaleViewForPrint:(NSView *)theView
{
   NSPrintInfo *printInfo = [self printInfo];
   float pageWidth = ([printInfo paperSize].width - ([printInfo leftMargin] + [printInfo rightMargin]));
   float pageHeight = ([printInfo paperSize].height - ([printInfo topMargin] + [printInfo bottomMargin]));
   float width, height, widthScale, heightScale;

   if(printPagesWide && printPagesHigh) { //we are scaling both width and height
      width = printPagesWide * pageWidth;
      height = printPagesHigh * pageHeight;
      widthScale = width / NSWidth([theView frame]);
      heightScale = height / NSHeight([theView frame]);

      [theView scaleUnitSquareToSize:NSMakeSize(widthScale, heightScale)];
      [theView setFrameSize:NSMakeSize((NSWidth([theView frame]) * widthScale), (NSHeight([theView frame]) * heightScale))];
   } else {
      if(printPagesWide) { //we are only setting a width
         width = printPagesWide * pageWidth;
         widthScale = width / NSWidth([theView frame]);

         [theView scaleUnitSquareToSize:NSMakeSize(widthScale, widthScale)];
         [theView setFrameSize:NSMakeSize((NSWidth([theView frame]) * widthScale), (NSHeight([theView frame]) * widthScale))];
      } else {
         if(printPagesHigh) { //we are only setting the height
            height = printPagesHigh * pageHeight;
            heightScale = height / NSHeight([theView frame]);
            [theView scaleUnitSquareToSize:NSMakeSize(heightScale, heightScale)];
            [theView setFrameSize:NSMakeSize((NSWidth([theView frame]) * heightScale), (NSHeight([theView frame]) * heightScale))];
         }
      }
   }
}

- (void)scalePrintViewsToMatchMatrixZoom
{
   //scale the views to match the current zoom
   float scale = (1 / [self chartViewScale]);
   [printChartView scaleUnitSquareToSize:NSMakeSize(scale, scale)];

   [printTableView scaleUnitSquareToSize:NSMakeSize(scale, scale)];

   [printDateDisplayView scaleUnitSquareToSize:NSMakeSize(scale, scale)];

   [tableView sizeLastColumnToFit];
}

- (void)printShowingPrintPanel:(BOOL)flag {
   NSPrintInfo *printInfo = [self printInfo];
   NSPrintOperation *printOp;

   //set the margins
   [printInfo setLeftMargin:(([printInfo paperSize].width - NSWidth([printInfo imageablePageBounds])) / 2)];
   [printInfo setRightMargin:(([printInfo paperSize].width - NSWidth([printInfo imageablePageBounds])) / 2)];
   [printInfo setTopMargin:(([printInfo paperSize].height - NSHeight([printInfo imageablePageBounds])) / 2)];
   [printInfo setBottomMargin:(([printInfo paperSize].height - NSHeight([printInfo imageablePageBounds])) / 2)];

   if([[NSApp keyWindow] isEqualTo:[[SSEBPanelController sharedEBPanelController] window]]) {
      //we are printing the eb window
      printOp = [NSPrintOperation printOperationWithView:[self printViewForEventPanel]
                                               printInfo:printInfo];
      [printOp setShowPanels:flag];
      [printOp runOperation];
   } else if([[NSApp keyWindow] isEqualTo:[[SSCommentPanelController sharedCommentPanelController] window]]) {
      //we are printing the comment window
      printOp = [NSPrintOperation printOperationWithView:[self printViewForCommentPanel]
                                               printInfo:printInfo];
      [printOp setShowPanels:flag];
      [printOp runOperation];
   } else if([[NSApp keyWindow] isEqualTo:[[SSNeedsDiamondsPanelController sharedDiamondPanelController] window]]) {
      //we are printing the diamond
      printOp = [NSPrintOperation printOperationWithView:[self printViewForDiamondPanel]
                                               printInfo:printInfo];
      [printOp setShowPanels:flag];
      [printOp runOperation];
   } else {
      //we are printing the main window
      NSEnumerator *eventEnumerator = [[chartView eventViews] objectEnumerator];
      SSEventView *anEvent;
      NSEnumerator *commentEnumerator = [[chartView commentViews] objectEnumerator];
      SSCommentView *aComment;
      SSEventView *eventView;
      SSCommentView *commentView;
      BOOL showLedgend = [[NSUserDefaults standardUserDefaults] boolForKey:SSDisplayChartLegendKey];
      float xPosModifier = 0.00;

      [printInfo setHorizontalPagination:NSAutoPagination];

      [NSBundle loadNibNamed:@"PrintWindow" owner:self]; //load the print nib

      [(SSChartView *)printChartView setDisplayMode:[self displayMode]];
      [printChartView setOverlapViewFromDate:[chartView overlapViewFromDate]];
      [printChartView setOverlapUnit:[chartView overlapUnit]];
      [printChartView setOverlapPixelsPerUnit:[chartView overlapPixelsPerUnit]];
      
      if(!showLedgend) {
         //we do not want to display the ledgend so remove it from the print window
         [printLegendView removeFromSuperview];
         [printWindow setContentSize:NSMakeSize(NSWidth([[printWindow contentView] frame]), (NSHeight([[printWindow contentView] frame]) - NSHeight([printLegendView frame])))];
      }
  
      while ((anEvent = [eventEnumerator nextObject])) {
         //build new event view based on the displayed eventView
         eventView = [[[SSEventView alloc] initWithEventView:anEvent] autorelease];
         [printChartView addEventView:eventView];
      }

      while ((aComment = [commentEnumerator nextObject])) {
         //build new comment view based on the displayed commentView
         commentView = [[[SSCommentView alloc] initWithCommentView:aComment] autorelease];
         [printChartView addCommentView:commentView];
      }

      if([self displayMode] == SSMatrixDisplayMode) {
         if([objectFilterController isFiltering]) {
            //we are filtering so move the ebs/comments
            float tempXPosModifier = 1000000.00;
            int i = 0;
            NSMutableArray *subviewArray = [NSMutableArray array];
            [subviewArray addObjectsFromArray:[printChartView eventViews]];
            [subviewArray addObjectsFromArray:[printChartView commentViews]];

            while([subviewArray count] > i) {
               //loop through all of the subviews and get the shortest distance to the left
               if(NSMinX([[subviewArray objectAtIndex:i] frame]) < tempXPosModifier) {
                  //this view is the closest so far so set xPosModifier
                  tempXPosModifier = NSMinX([[subviewArray objectAtIndex:i] frame]);
                  xPosModifier = tempXPosModifier;
               }

               i++;
            }
            xPosModifier = (xPosModifier * (1 / [self chartViewScale]));
            [printChartView setBoundsOrigin:NSMakePoint(xPosModifier, 0)]; //change the bounds origin so that the ebs are moved to the left
         }

         [printChartView sizeToFit]; //size the printview
      }


      [printTableView setRowHeight:[tableView rowHeight]];
      [[printTableView headerView] setFrameSize:NSMakeSize(NSWidth([[printTableView headerView] frame]), NSHeight([[tableView headerView] frame]))];

      if([self displayMode] == SSOverlapDisplayMode) {
         //add a custom cell so that we can draw the bigger size
         BigTableHeaderCell *cell = [[[BigTableHeaderCell alloc] init] autorelease];
         NSTableColumn *theColumn = [[printTableView tableColumns] objectAtIndex:0];
         [cell setTitle:[[theColumn headerCell] title]];
         [cell setAlignment:NSLeftTextAlignment];
         [theColumn setHeaderCell:cell];
      }
      
      [[[[printTableView tableColumns] objectAtIndex:0] dataCell] setWraps:YES];
      [[[[printTableView tableColumns] objectAtIndex:0] dataCell] setFont:[NSFont fontWithName:@"Helvetica" size:10]];

      if(showLedgend) { //we need to size differently depending on whether we are displaying the legend
         [printWindow setContentSize:NSMakeSize(NSWidth([tableView frame]) + (NSWidth([chartView frame]) - xPosModifier), (NSHeight([chartView frame]) + NSHeight([dateDisplayView frame]) + NSHeight([legendView frame]) + 1))];
      } else {
         [printWindow setContentSize:NSMakeSize(NSWidth([tableView frame]) + (NSWidth([chartView frame]) - xPosModifier), NSHeight([chartView frame]) + NSHeight([dateDisplayView frame]))];
      }      

      [self scalePrintViewsToMatchMatrixZoom];
      
      if(showLedgend) { //we need to size differently depending on whether we are displaying the legend
         [printWindow setContentSize:NSMakeSize(NSWidth([tableView frame]) + (NSWidth([printChartView frame]) - xPosModifier), (NSHeight([chartView frame]) + NSHeight([dateDisplayView frame]) + NSHeight([printLegendView frame]) + 1))];
      } else {
         [printWindow setContentSize:NSMakeSize(NSWidth([tableView frame]) + (NSWidth([printChartView frame]) - xPosModifier), NSHeight([chartView frame]) + NSHeight([dateDisplayView frame]))];
      }

      [[[printTableView superview] superview] setFrame:NSMakeRect(0, 0, NSWidth([tableView frame]),
                                                                  NSHeight([chartView frame]) + NSHeight([dateDisplayView frame]))]; //resize tableview scrollview

      [printDateDisplayView setFrame:NSMakeRect((NSMaxX([printTableView frame]) + 1), NSHeight([chartView frame]),
                                                NSWidth([chartView frame]), NSHeight([dateDisplayView frame]))]; //resize and move the view

      [printChartView setFrame:NSMakeRect((NSMaxX([printTableView frame]) + 1), 0,
                                          NSWidth([chartView frame]), NSHeight([chartView frame]))]; //resize and move the view   

      
      if(printPagesWide || printPagesHigh) { //user wants to scale to num pages
         [self scaleViewForPrint:[printWindow contentView]];
      }

      [printChartView display];

      [printWindow display];
      printOp = [NSPrintOperation printOperationWithView:[printWindow contentView]
                                               printInfo:printInfo];
      [printOp setShowPanels:flag];
      [printOp runOperation];

      [printWindow close];
   }
}

- (SSTablePrintView *)printViewForEventPanel
{
   NSMutableArray *columnArray = [NSMutableArray array];

   NSEnumerator *enumerator = [[[[SSEBPanelController sharedEBPanelController] tableView] tableColumns] objectEnumerator];
   id anObject;

   //enumerate through the table and build a dictionay for each of the displayed columns
   while (anObject = [enumerator nextObject]) {
      NSMutableDictionary *dict = [NSMutableDictionary dictionary];
      if([[anObject identifier] isEqualToString:@"label"]) {
         [dict setObject:[[anObject headerCell] stringValue] forKey:@"title"];
         [dict setObject:[NSNumber numberWithFloat:([anObject width] / NSWidth([[[SSEBPanelController sharedEBPanelController] tableView] frame]))]
                  forKey:@"percentOfPageWidth"];
         [dict setObject:@"labelString" forKey:@"dataAccessor"];
         [columnArray addObject:dict];
      } else if([[anObject identifier] isEqualToString:@"beginDate"]) {
         [dict setObject:[[anObject headerCell] stringValue] forKey:@"title"];
         [dict setObject:[NSNumber numberWithFloat:([anObject width] / NSWidth([[[SSEBPanelController sharedEBPanelController] tableView] frame]))]
                  forKey:@"percentOfPageWidth"];
         [dict setObject:@"beginTimeString" forKey:@"dataAccessor"];
         [columnArray addObject:dict];
      } else if([[anObject identifier] isEqualToString:@"endDate"]) {
         [dict setObject:[[anObject headerCell] stringValue] forKey:@"title"];
         [dict setObject:[NSNumber numberWithFloat:([anObject width] / NSWidth([[[SSEBPanelController sharedEBPanelController] tableView] frame]))]
                  forKey:@"percentOfPageWidth"];
         [dict setObject:@"endTimeString" forKey:@"dataAccessor"];
         [columnArray addObject:dict];
      } else if([[anObject identifier] isEqualToString:@"xPosition"]) {
         [dict setObject:[[anObject headerCell] stringValue] forKey:@"title"];
         [dict setObject:[NSNumber numberWithFloat:([anObject width] / NSWidth([[[SSEBPanelController sharedEBPanelController] tableView] frame]))]
                  forKey:@"percentOfPageWidth"];
         [dict setObject:@"xPositionString" forKey:@"dataAccessor"];
         [columnArray addObject:dict];
      } else {
         //the returned value is going to be a string so just use the normal identifier
         [dict setObject:[[anObject headerCell] stringValue] forKey:@"title"];
         [dict setObject:[NSNumber numberWithFloat:([anObject width] / NSWidth([[[SSEBPanelController sharedEBPanelController] tableView] frame]))]
                  forKey:@"percentOfPageWidth"];
         [dict setObject:[anObject identifier] forKey:@"dataAccessor"];
         [columnArray addObject:dict];
      }
   }
   
   SSTablePrintView *p = [[[SSTablePrintView alloc] initWithDataArray:[[SSEBPanelController sharedEBPanelController] dataArray] columnArray:columnArray printInfo:[self printInfo]] autorelease];

   return p;
}

- (SSTablePrintView *)printViewForCommentPanel
{
   NSMutableArray *columnArray = [NSMutableArray array];

   NSEnumerator *enumerator = [[[[SSCommentPanelController sharedCommentPanelController] tableView] tableColumns] objectEnumerator];
   id anObject;

   //enumerate through the table and build a dictionay for each of the displayed columns
   while (anObject = [enumerator nextObject]) {
      NSMutableDictionary *dict = [NSMutableDictionary dictionary];
      if([[anObject identifier] isEqualToString:@"xPosition"]) {
         [dict setObject:[[anObject headerCell] stringValue] forKey:@"title"];
         [dict setObject:[NSNumber numberWithFloat:([anObject width] / NSWidth([[[SSCommentPanelController sharedCommentPanelController] tableView] frame]))]
                  forKey:@"percentOfPageWidth"];
         [dict setObject:@"xPositionString" forKey:@"dataAccessor"];
         [columnArray addObject:dict];
      } else if([[anObject identifier] isEqualToString:@"yPosition"]) {
         [dict setObject:[[anObject headerCell] stringValue] forKey:@"title"];
         [dict setObject:[NSNumber numberWithFloat:([anObject width] / NSWidth([[[SSCommentPanelController sharedCommentPanelController] tableView] frame]))]
                  forKey:@"percentOfPageWidth"];
         [dict setObject:@"yPositionString" forKey:@"dataAccessor"];
         [columnArray addObject:dict];
      } else if([[anObject identifier] isEqualToString:@"type"]) {
         [dict setObject:[[anObject headerCell] stringValue] forKey:@"title"];
         [dict setObject:[NSNumber numberWithFloat:([anObject width] / NSWidth([[[SSCommentPanelController sharedCommentPanelController] tableView] frame]))]
                  forKey:@"percentOfPageWidth"];
         [dict setObject:@"typeString" forKey:@"dataAccessor"];
         [columnArray addObject:dict];
      } else {
         //the returned value is going to be a string so just use the normal identifier
         [dict setObject:[[anObject headerCell] stringValue] forKey:@"title"];
         [dict setObject:[NSNumber numberWithFloat:([anObject width] / NSWidth([[[SSCommentPanelController sharedCommentPanelController] tableView] frame]))]
                  forKey:@"percentOfPageWidth"];
         [dict setObject:[anObject identifier] forKey:@"dataAccessor"];
         [columnArray addObject:dict];
      }
   }

   SSTablePrintView *p = [[[SSTablePrintView alloc] initWithDataArray:[[SSCommentPanelController sharedCommentPanelController] dataArray] columnArray:columnArray printInfo:[self printInfo]] autorelease];

   return p;
}

- (NSView *)printViewForDiamondPanel
{
   NSMutableArray *diamondColumnArray = [NSMutableArray array];
   NSMutableArray *optionColumnArray = [NSMutableArray array];
   NSMutableArray *tradeoffColumnArray = [NSMutableArray array];
   SSTablePrintView *diamondPrintView = nil;
   SSTablePrintView *optionPrintView = nil;
   SSTablePrintView *tradeoffPrintView = nil;
   NSView *tableGroupView;
   SSNeedsDiamondsPanelController *diamondPanel = [SSNeedsDiamondsPanelController sharedDiamondPanelController];

   NSEnumerator *enumerator = [[[diamondPanel diamondTable] tableColumns] objectEnumerator];
   id anObject;

   //enumerate through the table and build a dictionay for each of the displayed columns
   while (anObject = [enumerator nextObject]) {
      NSMutableDictionary *dict = [NSMutableDictionary dictionary];
      if([[anObject identifier] isEqualToString:@"number"]) {
         [dict setObject:[[anObject headerCell] stringValue] forKey:@"title"];
         [dict setObject:[NSNumber numberWithFloat:([anObject width] / NSWidth([[diamondPanel diamondTable] frame]))]
                  forKey:@"percentOfPageWidth"];
         [dict setObject:@"numberString" forKey:@"dataAccessor"];
         [diamondColumnArray addObject:dict];
      } else if([[anObject identifier] isEqualToString:@"diamondState"]) {
         [dict setObject:[[anObject headerCell] stringValue] forKey:@"title"];
         [dict setObject:[NSNumber numberWithFloat:([anObject width] / NSWidth([[diamondPanel diamondTable] frame]))]
                  forKey:@"percentOfPageWidth"];
         [dict setObject:@"diamondStateString" forKey:@"dataAccessor"];
         [diamondColumnArray addObject:dict];
      } else {
         //the returned value is going to be a string so just use the normal identifier
         [dict setObject:[[anObject headerCell] stringValue] forKey:@"title"];
         [dict setObject:[NSNumber numberWithFloat:([anObject width] / NSWidth([[diamondPanel diamondTable] frame]))]
                  forKey:@"percentOfPageWidth"];
         [dict setObject:[anObject identifier] forKey:@"dataAccessor"];
         [diamondColumnArray addObject:dict];
      }
   }

   diamondPrintView = [[[SSTablePrintView alloc] initWithDataArray:[diamondPanel dataArray] columnArray:diamondColumnArray printInfo:[self printInfo]] autorelease];

   if([[diamondPanel diamondTable] selectedRow] != -1) {
      //there is a selection so do the option table
      enumerator = [[[diamondPanel optionTable] tableColumns] objectEnumerator];
      //enumerate through the option table and build a dictionay for each of the displayed columns
      while (anObject = [enumerator nextObject]) {
         NSMutableDictionary *dict = [NSMutableDictionary dictionary];
         if(![[anObject identifier] isEqualToString:@"index"]) {
            //the returned value is going to be a string so just use the normal identifier
            [dict setObject:[[anObject headerCell] stringValue] forKey:@"title"];
            [dict setObject:[NSNumber numberWithFloat:([anObject width] / NSWidth([[diamondPanel optionTable] frame]))]
                     forKey:@"percentOfPageWidth"];
            [dict setObject:[anObject identifier] forKey:@"dataAccessor"];
            [optionColumnArray addObject:dict];
         }
      }

      optionPrintView = [[[SSTablePrintView alloc] initWithDataArray:[[[diamondPanel dataArray] objectAtIndex:[[diamondPanel diamondTable] selectedRow]] optionArray] columnArray:optionColumnArray printInfo:[self printInfo]] autorelease];

      if([[diamondPanel optionTable] selectedRow] != -1) {
         //there is a selection so do the tradeoff table
         enumerator = [[[diamondPanel tradeoffTable] tableColumns] objectEnumerator];
         //enumerate through the option table and build a dictionay for each of the displayed columns
         while (anObject = [enumerator nextObject]) {
            NSMutableDictionary *dict = [NSMutableDictionary dictionary];
            if(![[anObject identifier] isEqualToString:@"index"]) {
               //the returned value is going to be a string so just use the normal identifier
               [dict setObject:[[anObject headerCell] stringValue] forKey:@"title"];
               [dict setObject:[NSNumber numberWithFloat:([anObject width] / NSWidth([[diamondPanel tradeoffTable] frame]))]
                        forKey:@"percentOfPageWidth"];
               [dict setObject:[anObject identifier] forKey:@"dataAccessor"];
               [tradeoffColumnArray addObject:dict];
            }
         }

         tradeoffPrintView = [[[SSTablePrintView alloc] initWithDataArray:[[[[[diamondPanel dataArray] objectAtIndex:[[diamondPanel diamondTable] selectedRow]] optionArray] objectAtIndex:[[diamondPanel optionTable] selectedRow]] tradeoffArray] columnArray:tradeoffColumnArray printInfo:[self printInfo]] autorelease];
      }
   }

   if(optionPrintView) {
      if(tradeoffPrintView) {
         //all tables should be displayed
         tableGroupView = [[[NSView alloc] initWithFrame:NSMakeRect(0, 0, NSWidth([diamondPrintView frame]), (NSHeight([diamondPrintView frame]) + NSHeight([optionPrintView frame]) + NSHeight([tradeoffPrintView frame]) + 10))] autorelease];
         [diamondPrintView setFrameOrigin:NSMakePoint(0, NSHeight([tableGroupView frame]) - NSHeight([diamondPrintView frame]))];
         [tableGroupView addSubview:diamondPrintView];
         
         [optionPrintView setFrameOrigin:NSMakePoint(0, (NSMinY([diamondPrintView frame]) - NSHeight([optionPrintView frame]) - 5))];
         [tableGroupView addSubview:optionPrintView];

         [tradeoffPrintView setFrameOrigin:NSMakePoint(0, (NSMinY([optionPrintView frame]) - NSHeight([tradeoffPrintView frame]) - 5))];
         [tableGroupView addSubview:tradeoffPrintView];
         return tableGroupView;
      } else {
         //there are diamond and option tables
         tableGroupView = [[[NSView alloc] initWithFrame:NSMakeRect(0, 0, NSWidth([diamondPrintView frame]), (NSHeight([diamondPrintView frame]) + NSHeight([optionPrintView frame]) + 5))] autorelease];

         [diamondPrintView setFrameOrigin:NSMakePoint(0, NSHeight([tableGroupView frame]) - NSHeight([diamondPrintView frame]))];
         [tableGroupView addSubview:diamondPrintView];

         [optionPrintView setFrameOrigin:NSMakePoint(0, (NSMinY([diamondPrintView frame]) - NSHeight([optionPrintView frame]) - 5))];
         [tableGroupView addSubview:optionPrintView];
         return tableGroupView;
      }
   } else {
      //there are only diamonds diaplayed
      return diamondPrintView;
   }
}

- (NSBitmapImageRep *)chartImage
{
   NSBitmapImageRep *sourceBitmap;
   NSEnumerator *eventEnumerator = [[chartView eventViews] objectEnumerator];
   SSEventView *anEvent;
   NSEnumerator *commentEnumerator = [[chartView commentViews] objectEnumerator];
   SSCommentView *aComment;
   SSEventView *eventView;
   SSCommentView *commentView;
   BOOL showLedgend = [[NSUserDefaults standardUserDefaults] boolForKey:SSDisplayChartLegendKey];
   float xPosModifier = 0.00;

   [NSBundle loadNibNamed:@"PrintWindow" owner:self]; //load the print nib

   [(SSChartView *)printChartView setDisplayMode:[self displayMode]];
   [printChartView setOverlapViewFromDate:[chartView overlapViewFromDate]];
   [printChartView setOverlapUnit:[chartView overlapUnit]];
   [printChartView setOverlapPixelsPerUnit:[chartView overlapPixelsPerUnit]];
   
   if(!showLedgend) {
      //we don't want to display the ledgend so remove it from the print window
      [printLegendView removeFromSuperview];
      [printWindow setContentSize:NSMakeSize(NSWidth([[printWindow contentView] frame]), (NSHeight([[printWindow contentView] frame]) - NSHeight([printLegendView frame])))];
   }

   while ((anEvent = [eventEnumerator nextObject])) {
      //build new event view based on the displayed eventView
      eventView = [[[SSEventView alloc] initWithEventView:anEvent] autorelease];
      [printChartView addEventView:eventView];
   }

   while ((aComment = [commentEnumerator nextObject])) {
      //build new comment view based on the displayed commentView
      commentView = [[[SSCommentView alloc] initWithCommentView:aComment] autorelease];
      [printChartView addCommentView:commentView];
   }

   if([self displayMode] == SSMatrixDisplayMode) {
      //in matrix
      if([objectFilterController isFiltering]) {
         //we are filtering so move the ebs/comments
         float tempXPosModifier = 1000000.00;
         int i = 0;
         NSMutableArray *subviewArray = [NSMutableArray array];
         [subviewArray addObjectsFromArray:[printChartView eventViews]];
         [subviewArray addObjectsFromArray:[printChartView commentViews]];

         while([subviewArray count] > i) {
            //loop through all of the subviews and get the shortest distance to the left
            if(NSMinX([[subviewArray objectAtIndex:i] frame]) < tempXPosModifier) {
               //this view is the closest so far so set xPosModifier
               tempXPosModifier = NSMinX([[subviewArray objectAtIndex:i] frame]);
               xPosModifier = tempXPosModifier;
            }

            i++;
         }
         xPosModifier = (xPosModifier * (1 / [self chartViewScale]));
         [printChartView setBoundsOrigin:NSMakePoint(xPosModifier, 0)]; //change the bounds origin so that the ebs are moved to the left
      }
      [printChartView sizeToFit]; //size the printview
   }

   [printTableView setRowHeight:[tableView rowHeight]];
   [[printTableView headerView] setFrameSize:NSMakeSize(NSWidth([[printTableView headerView] frame]), NSHeight([[tableView headerView] frame]))];

   if([self displayMode] == SSOverlapDisplayMode) {
      //add a custom cell so that we can draw the bigger size
      BigTableHeaderCell *cell = [[[BigTableHeaderCell alloc] init] autorelease];
      NSTableColumn *theColumn = [[printTableView tableColumns] objectAtIndex:0];
      [cell setTitle:[[theColumn headerCell] title]];
      [cell setAlignment:NSLeftTextAlignment];
      [theColumn setHeaderCell:cell];
   }

   [[[[printTableView tableColumns] objectAtIndex:0] dataCell] setWraps:YES];
   [[[[printTableView tableColumns] objectAtIndex:0] dataCell] setFont:[NSFont fontWithName:@"Helvetica" size:10]];
   
   if(showLedgend) { //we need to size differently depending on whether we are displaying the legend
      [printWindow setContentSize:NSMakeSize(NSWidth([tableView frame]) + (NSWidth([chartView frame]) - xPosModifier), (NSHeight([chartView frame]) + NSHeight([dateDisplayView frame]) + NSHeight([legendView frame]) + 1))];
   } else {
      [printWindow setContentSize:NSMakeSize(NSWidth([tableView frame]) + (NSWidth([chartView frame]) - xPosModifier), NSHeight([chartView frame]) + NSHeight([dateDisplayView frame]))];
   }

   [self scalePrintViewsToMatchMatrixZoom]; //scale the matrix

   [[[printTableView superview] superview] setFrame:NSMakeRect(0, 0, NSWidth([tableView frame]),
                                                               NSHeight([chartView frame]) + NSHeight([dateDisplayView frame]))]; //resize tableview scrollview

   [printDateDisplayView setFrame:NSMakeRect((NSMaxX([printTableView frame]) + 1), NSHeight([chartView frame]),
                                             NSWidth([chartView frame]), NSHeight([dateDisplayView frame]))]; //resize and move the view

   [printChartView setFrame:NSMakeRect((NSMaxX([printTableView frame]) + 1), 0,
                                       NSWidth([chartView frame]), NSHeight([chartView frame]))]; //resize and move the view

   [printChartView display];
   [printTableView reloadData];

   [printWindow display];

   [[printWindow contentView] lockFocus];
   sourceBitmap = [[NSBitmapImageRep alloc]
         initWithFocusedViewRect:[[printWindow contentView] frame]];
   [[printWindow contentView] unlockFocus];
   [sourceBitmap autorelease];

   [printWindow close];

   return sourceBitmap;
}

- (NSData *)glossaryDataForType:(NSString *)type
{
   return [documentData glossaryDataForType:type];
}

- (NSData *)optionGlossaryData
{
   return [documentData optionGlossaryData];
}

- (NSData *)proTradeoffGlossaryData
{
   return [documentData proTradeoffGlossaryData];
}

- (NSData *)conTradeoffGlossaryData
{
   return [documentData conTradeoffGlossaryData];   
}

- (NSData *)remarkListData
{
   return [documentData remarkListData];
}

- (NSData *)eventListData
{
   return [documentData eventListData];
}

- (NSData *)commentListData
{
   return [documentData commentListData];
}

- (NSData *)diamondListData
{
   return [documentData diamondListData];
}

- (NSMutableArray *)poisonWordArray
{
   return [documentData poisonWordArray];
}

- (void)addPoisonWord:(SSPoisonWord *)word
{
   [documentData addPoisonWord:word];
}

- (void)removePoisonWord:(SSPoisonWord *)word
{
   [documentData removePoisonWord:word];
}

- (void)sendExportNote
{
   //sent to the SSCatalystAppController
   [[NSNotificationCenter defaultCenter] postNotificationName:@"SSExportChartImage" object:self];
}

//***********************************
- (SSObjectFilterController *)objectFilterController
{
   return objectFilterController;   
}

//***********************************Jump map view methods
- (SSJumpMapView *)jumpMapView
{
   return jumpMapView;   
}

- (void)addJumpMapToChartView
{
   [chartView addJumpMapView:jumpMapView];   
}

- (void)removeJumpMapFromChartView
{
   [jumpMapView removeFromSuperview];
   [chartView setNeedsDisplayInRect:[chartView visibleRect]];
}

//***********************************legend view methods
- (void)setLegendViewDisplay
{
   if([[NSUserDefaults standardUserDefaults] boolForKey:SSDisplayChartLegendKey]) {
      //we want to display the ledgend
      [self addLegendView];
   } else {
      [self removeLegendView];
   }
}

- (void)addLegendView
{
   if(![legendView superview]) {
      //the legendView is not currently displayed
      NSView *contentView = [[[[self windowControllers] objectAtIndex:0] window] contentView];
      [contentView addSubview:legendView];
      [legendView setFrame:NSMakeRect(0, (NSHeight([contentView frame]) - NSHeight([legendView frame])), NSWidth([contentView frame]), NSHeight([legendView frame]))]; //set the legend view frame

      //move and resize the split view
      [splitView setFrame:NSMakeRect(0, 0, NSWidth([splitView frame]), (NSHeight([splitView frame]) - NSHeight([legendView frame])))];

      [contentView setNeedsDisplay:YES];
      [legendView release];
   }
}

- (void)removeLegendView
{
   if([legendView superview]) {
      //the legendView is currently being displayed
      NSView *contentView = [[[[self windowControllers] objectAtIndex:0] window] contentView];
      
      [legendView retain];
      [legendView removeFromSuperview];

      //move and resize the split view
      [splitView setFrame:NSMakeRect(0, 0, NSWidth([splitView frame]), NSHeight([contentView frame]))];

      [contentView setNeedsDisplay:YES];
   }
}

//***********************************

- (void)showInspectorPanel
{
   [[SSInspectorController sharedInspectorController] showWindow:self];
}

- (void)showObjectFilterPanel
{
   [[SSObjectFilterPanelController sharedObjectFilterPanelController] showWindow:self];
}

//***********************************table view data source

- (int)numberOfRowsInTableView:(NSTableView *)aTableView
{
   return [[documentData filteredActorArray] count];
}

- (id)tableView:(NSTableView *)aTableView
objectValueForTableColumn:(NSTableColumn *)aTableColumn
            row:(int)rowIndex
{
   return [[documentData filteredActorArray] objectAtIndex:rowIndex];
}

//-----drag and drop
- (BOOL)tableView:(NSTableView *)tv writeRows:(NSArray*)rows
     toPasteboard:(NSPasteboard*)pboard
{
    if([objectFilterController isFiltering]) {
        return NO;
    } else {
        [pboard declareTypes:[NSArray arrayWithObject:PrivateDragPboard] owner:self];
        [pboard setData:[NSData data] forType:PrivateDragPboard];
        draggedRows = rows;

        return YES;
    }
}

- (NSDragOperation)tableView:(NSTableView*)tv validateDrop:(id<NSDraggingInfo>)info proposedRow:(int)row
       proposedDropOperation:(NSTableViewDropOperation)op
{
   if (op==NSTableViewDropAbove) {
      return NSDragOperationAll;
   }
    
   return NSDragOperationNone;
}

- (BOOL)tableView:(NSTableView*)tv acceptDrop:(id <NSDraggingInfo>)info
              row:(int)row dropOperation:(NSTableViewDropOperation)op
{
   [documentData dataSourceDictionaryArrayWithKey:@"actor" moveObjectsAtIndexes:draggedRows toIndex:row];//move the object to its new position
   [documentData updateFilteredActorArray];
   
   [tableView deselectAll:self];
   [tableView reloadData];

   [chartView recalculateEBPositions];

   [self updateChangeCount:NSChangeDone];
      
   return NSDragOperationAll;
}


//----------------------------------- for alternating row colors
- (void)tableView:(NSTableView *)theTableView
  willDisplayCell:(id)theCell forTableColumn:(NSTableColumn *)theColumn
              row:(int)rowIndex
{
   if((rowIndex % 2) == 0) {
      [theCell setDrawsBackground:YES];
      [theCell setBackgroundColor:[NSColor colorWithCalibratedRed:0.95 green:0.97 blue:1 alpha:1]];
   } else {
      [theCell setDrawsBackground:NO];
      [theCell setBackgroundColor:[NSColor whiteColor]];
   }
}

- (void)prepareViewsForMatrixDisplay
{
   int num = SSMinNumberOfEBFields;
   float chartViewHeightModifier = NSHeight([dateDisplayView frame]);
   NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];

   //set the headerview size
   [[tableView headerView] setFrameSize:NSMakeSize(NSWidth([[tableView headerView] frame]), SSMatrixDisplayTableHeaderHeight)];

   //add standard cell now that we are displaying at normal size
   NSTableHeaderCell *cell = [[[NSTableHeaderCell alloc] init] autorelease];
   NSTableColumn *theColumn = [[tableView tableColumns] objectAtIndex:0];
   [cell setTitle:[[theColumn headerCell] title]];
   [cell setAlignment:NSLeftTextAlignment];
   [theColumn setHeaderCell:cell];

   //update the tableView
   [[tableView enclosingScrollView] setFrame:NSMakeRect(NSMinX([[tableView enclosingScrollView] frame]), NSMinY([[tableView enclosingScrollView] frame]), NSWidth([[tableView enclosingScrollView] frame]) + 1, NSHeight([[tableView enclosingScrollView] frame]))];
   [[tableView enclosingScrollView] setFrame:NSMakeRect(NSMinX([[tableView enclosingScrollView] frame]), NSMinY([[tableView enclosingScrollView] frame]), NSWidth([[tableView enclosingScrollView] frame]) - 1, NSHeight([[tableView enclosingScrollView] frame]))];
   [tableView setNeedsDisplay:YES];

   //set the date display height
   [dateDisplayView setFrame:NSMakeRect(NSMinX([dateDisplayView frame]), (NSHeight([splitView frame]) - SSMatrixDisplayTableHeaderHeight - 1), NSWidth([dateDisplayView frame]), SSMatrixDisplayTableHeaderHeight)];

   chartViewHeightModifier -= NSHeight([dateDisplayView frame]); //so that we know how much to change chartView
   //set the chartView height
   [[chartView enclosingScrollView] setFrameSize:NSMakeSize(NSWidth([[chartView enclosingScrollView] frame]), (NSHeight([[chartView enclosingScrollView] frame]) + chartViewHeightModifier))];
   
   if([defaults boolForKey:SSDisplayEBActorKey]) {
      num++;
   }

   if([defaults boolForKey:SSDisplayEBSourceKey]) {
      num++;
   }

   if([defaults boolForKey:SSDisplayEBObjectKey]) {
      num++;
   }

   if([defaults boolForKey:SSDisplayEBLocationKey]) {
      num++;
   }

   if([defaults boolForKey:SSDisplayEBBeginDateKey]) {
      num++;
   }

   if([defaults boolForKey:SSDisplayEBEndDateKey]) {
      num++;
   }

   [tableView setRowHeight:((num * SSDistanceForEventFields) + 16)];

   //redisplay the window
   [[[[[self windowControllers] objectAtIndex:0] window] contentView] setNeedsDisplay:YES];
}

- (void)prepareViewsForOverlapDisplay
{
   NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
   float chartViewHeightModifier = NSHeight([dateDisplayView frame]);

   //set the headerview size
   [[tableView headerView] setFrameSize:NSMakeSize(NSWidth([[tableView headerView] frame]), SSOverlapDisplayTableHeaderHeight)];

   //add a custom cell so that we can draw the bigger size
   BigTableHeaderCell *cell = [[[BigTableHeaderCell alloc] init] autorelease];
   NSTableColumn *theColumn = [[tableView tableColumns] objectAtIndex:0];
   [cell setTitle:[[theColumn headerCell] title]];
   [cell setAlignment:NSLeftTextAlignment];
   [theColumn setHeaderCell:cell];

   //update the tableView
   [[tableView enclosingScrollView] setFrame:NSMakeRect(NSMinX([[tableView enclosingScrollView] frame]), NSMinY([[tableView enclosingScrollView] frame]), NSWidth([[tableView enclosingScrollView] frame]) + 1, NSHeight([[tableView enclosingScrollView] frame]))];
   [[tableView enclosingScrollView] setFrame:NSMakeRect(NSMinX([[tableView enclosingScrollView] frame]), NSMinY([[tableView enclosingScrollView] frame]), NSWidth([[tableView enclosingScrollView] frame]) - 1, NSHeight([[tableView enclosingScrollView] frame]))];
   [tableView setNeedsDisplay:YES];

   //set the date display height
   [dateDisplayView setFrame:NSMakeRect(NSMinX([dateDisplayView frame]), (NSHeight([splitView frame]) - SSOverlapDisplayTableHeaderHeight - 1), NSWidth([dateDisplayView frame]), SSOverlapDisplayTableHeaderHeight)];

   chartViewHeightModifier -= NSHeight([dateDisplayView frame]); //so that we know how much to change chartView
                                                                 //set the chartView height
   [[chartView enclosingScrollView] setFrameSize:NSMakeSize(NSWidth([[chartView enclosingScrollView] frame]), (NSHeight([[chartView enclosingScrollView] frame]) + chartViewHeightModifier))];

   //redisplay the window
   [[[[[self windowControllers] objectAtIndex:0] window] contentView] setNeedsDisplay:YES];
   
   if([defaults boolForKey:SSDisplayEBBeginDateKey] || [defaults boolForKey:SSDisplayEBEndDateKey]) {
      //displaying the date
      [tableView setRowHeight:(SSEventViewOverlapModeWithDatesHeight + SSDistanceBetweenActorRows)];
   } else {
      //just displaying action
      [tableView setRowHeight:(SSEventViewOverlapModeNoDatesHeight + SSDistanceBetweenActorRows)];
   }
}

- (void)setTableViewRowHeight
{
   NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
   
   if([chartView displayMode] == SSMatrixDisplayMode) {
      int num = SSMinNumberOfEBFields;

      if([defaults boolForKey:SSDisplayEBActorKey]) {
         num++;
      }

      if([defaults boolForKey:SSDisplayEBSourceKey]) {
         num++;
      }

      if([defaults boolForKey:SSDisplayEBObjectKey]) {
         num++;
      }

      if([defaults boolForKey:SSDisplayEBLocationKey]) {
         num++;
      }

      if([defaults boolForKey:SSDisplayEBBeginDateKey]) {
         num++;
      }

      if([defaults boolForKey:SSDisplayEBEndDateKey]) {
         num++;
      }

      [tableView setRowHeight:((num * SSDistanceForEventFields) + 16)];
   } else {
      //in overlap
      if([defaults boolForKey:SSDisplayEBBeginDateKey] || [defaults boolForKey:SSDisplayEBEndDateKey]) {
         //displaying the date
         [tableView setRowHeight:(SSEventViewOverlapModeWithDatesHeight + SSDistanceBetweenActorRows)];
      } else {
         //just displaying action
         [tableView setRowHeight:(SSEventViewOverlapModeNoDatesHeight + SSDistanceBetweenActorRows)];
      }
   }
}

- (int)tableViewRowHeight
{
   return ([tableView rowHeight] + [tableView intercellSpacing].height);
}

- (float)actorColumnWidth
{
   return NSWidth([[tableView enclosingScrollView] frame]);
}

//***********************************
- (void)reload
{
   //reload the table and redisplay the chartView
   [tableView reloadData];
   [chartView setNeedsDisplayInRect:[chartView visibleRect]];
}

- (void)redisplay
{
   //redisplay the chartView
   [chartView setNeedsDisplayInRect:[chartView visibleRect]];
}

//***********************************
- (void)snapChartToTimes
{
   NSArray *sortedEBArray = [[documentData eventArray] sortedArrayUsingSelector:@selector(compareByBeginTime:)];
   NSMutableArray *subEBArray;
   NSMutableArray *previousEventArray;
   SSEventObject *firstEvent;
   SSEventObject *currentEvent;
   float xOrigin, tempXOrigin;
   NSUndoManager* aManager = [self undoManager];
   int i = 0;
   int j = 0;
   int k = 0;
   
   if([sortedEBArray count] > 0) {
      firstEvent = [sortedEBArray objectAtIndex:0];
   }

   [aManager beginUndoGrouping];
   while (i < [sortedEBArray count]) {
      xOrigin = 0;
      subEBArray = [NSMutableArray array];
      currentEvent = [sortedEBArray objectAtIndex:i];
      do {
         //build an array with the current EB and all other EBs with this events beginTime
         [subEBArray addObject:[sortedEBArray objectAtIndex:i]];
         i++;
      } while(i < [sortedEBArray count] && [[[sortedEBArray objectAtIndex:i] beginTime] compare:[currentEvent beginTime]] == NSOrderedSame);

      //we now have an array with all of the events at the beginTime lets set the position for them
      j = 0;
      if(![currentEvent isEqualTo:firstEvent]) {
         k = ([sortedEBArray indexOfObject:currentEvent] - 1);
         previousEventArray = [NSMutableArray array];
         [previousEventArray addObject:[sortedEBArray objectAtIndex:k]];
         do {
            //build the previousEventArray
            if([[[sortedEBArray objectAtIndex:k] beginTime] compare:[[previousEventArray objectAtIndex:0] beginTime]] == NSOrderedSame || [subEBArray containsObjectWithActor:[[sortedEBArray objectAtIndex:k] actor]]) {
               [previousEventArray addObject:[sortedEBArray objectAtIndex:k]];
            }
            k--;
         } while(k >= 0);

         xOrigin = ((float)[[previousEventArray objectAtIndex:0] xPosition] + (float)[chartView widthOfDateDisplayCells] + 5); //the minimum x origin assuming that there are no other ebs on the row
         while(j < [subEBArray count]) {
            //get the xPos for all events in array
            if([[subEBArray objectAtIndex:j] hasLinkFromSameActor]) {
               //this EB has a link from an eb with the same actor so take account of that
               tempXOrigin = ([[previousEventArray objectAtIndex:0] xPosition] + SSEventViewDefaultSize.width + 30);
               
               if(tempXOrigin > xOrigin) {
                  //tempXOrigin is greater than xOrigin so use that figure
                  xOrigin = tempXOrigin;
               }
            } else {
               //loop through and 
               k = 0;
               while(k < [previousEventArray count]) {
                  if([[[subEBArray objectAtIndex:j] actor] isEqualToString:[[previousEventArray objectAtIndex:k] actor]] ) {
                     //this eb is on the same row as one of the previous events so we need to take account of any diamonds or links
                     tempXOrigin = 0;
                    
                     if([[previousEventArray objectAtIndex:k] hasNeedsDiamonds]) {
                        //we need to account for needs diamonds
                        tempXOrigin = ([[previousEventArray objectAtIndex:k] xPosition] + SSEventViewDefaultSize.width + 30);
                     } else {
                        //we only need to account for the eb length + a few pixles
                        tempXOrigin = ([[previousEventArray objectAtIndex:k] xPosition] + SSEventViewDefaultSize.width + 3);
                     }

                     if(tempXOrigin > xOrigin) {
                        //tempXOrigin is greater than xOrigin so use that figure
                        xOrigin = tempXOrigin;
                     }
                  }
                  k++;
               }
            }
            j++;
         }
      } else {
         //it's the first event
         xOrigin = 0.0;
      }

      j = 0;
      while(j < [subEBArray count]) {
      	//loop through and set the EBs xPosition to xOrigin
         [aManager registerUndoWithTarget:[[subEBArray objectAtIndex:j] eventView] selector:@selector(setNewOriginWithValue:) object:[NSValue valueWithPoint:NSMakePoint(NSMinX([[[subEBArray objectAtIndex:j] eventView] frame]), NSMinY([[[subEBArray objectAtIndex:j] eventView] frame]))]];
         [[[subEBArray objectAtIndex:j] eventView] setFrameOrigin:NSMakePoint(xOrigin, NSMinY([[[subEBArray objectAtIndex:j] eventView] frame]))];
         j++;
      }
   }
   
   [aManager endUndoGrouping];

   [chartView sizeToFit];
   [chartView setNeedsDisplay:YES];

   [[NSNotificationCenter defaultCenter] postNotificationName:@"SSUpdateTrackingCellEllipseAndColor"
                                                       object:nil];
} 

- (void)snapTimesToChart
{
   NSArray *sortedEBArray = [[documentData eventArray] sortedArrayUsingSelector:@selector(compareByXPosition:)];
   NSEnumerator *enumerator = [sortedEBArray objectEnumerator];
   id anObject;
   SSEventObject *firstEvent;
   SSEventObject *previousEvent;
   NSUndoManager* aManager = [self undoManager];

   if([sortedEBArray count] > 0) {
      firstEvent = [sortedEBArray objectAtIndex:0];
   }

   [aManager beginUndoGrouping];
   while (anObject = [enumerator nextObject]) {
      if(![anObject isEqualTo:firstEvent]) { //we don't want to change the first event
         if([anObject xPosition] == [previousEvent xPosition]) {
            //it is in the same xPosition as the prievous event so use the same date
            [anObject setBeginTime:[[[previousEvent beginTime] copy] autorelease]];
            [anObject setBeginTimeStatus:YES];
         } else {
            //it is to the right of the prievous event so increment the date by 1 second
            [anObject setBeginTime:(SSChartDate *)[[previousEvent beginTime] dateByAddingYears:0 months:0 days:0 hours:0 minutes:0 seconds:1]];
            [anObject setBeginTimeStatus:YES];
         }
      }

      previousEvent = anObject;
   }
   [aManager endUndoGrouping];
   [chartView setNeedsDisplay:YES];
}

- (void)setIncrementedTimeForEBBasedOnPrevious:(NSMenuItem *)item
{
   NSArray *sortedEBArray = [[documentData eventArray] sortedArrayUsingSelector:@selector(compareByXPosition:)];
   SSEventObject *eventToSet = [item representedObject];
   int eventsArrayPos = [sortedEBArray indexOfObject:eventToSet];

   if(eventsArrayPos > 0) {
      if([eventToSet xPosition] == [[sortedEBArray objectAtIndex:(eventsArrayPos - 1)] xPosition]) {
         //the eb to the left is at the same x position as the eb to set so use it's date
         [eventToSet setBeginTime:[[[[sortedEBArray objectAtIndex:(eventsArrayPos - 1)] beginTime] copy] autorelease]];
         [eventToSet setBeginTimeStatus:YES];
         return;
      }
   }

   if(![eventToSet isEqualTo:[sortedEBArray lastObject]]) {
      if([eventToSet xPosition] == [[sortedEBArray objectAtIndex:(eventsArrayPos + 1)] xPosition]) {
         //the eb to the right is at the same x position as the eb to set so use it's date
         [eventToSet setBeginTime:[[[[sortedEBArray objectAtIndex:(eventsArrayPos + 1)] beginTime] copy] autorelease]];
         [eventToSet setBeginTimeStatus:YES];
         return;
      }
   }

   if(eventsArrayPos > 0) {
      //increment 1 second over the left eb
      [eventToSet setBeginTime:(SSChartDate *)[[[sortedEBArray objectAtIndex:(eventsArrayPos - 1)] beginTime] dateByAddingYears:0 months:0 days:0 hours:0 minutes:0 seconds:1]];
      [eventToSet setBeginTimeStatus:YES];
   }

   [chartView setNeedsDisplayInRect:[chartView visibleRect]];
}

- (void)setIncrementedTimeForEBBasedOnNext:(NSMenuItem *)item
{
   NSArray *sortedEBArray = [[documentData eventArray] sortedArrayUsingSelector:@selector(compareByXPosition:)];
   SSEventObject *eventToSet = [item representedObject];
   int eventsArrayPos = [sortedEBArray indexOfObject:eventToSet];

   if(eventsArrayPos > 0) {
      if([eventToSet xPosition] == [[sortedEBArray objectAtIndex:(eventsArrayPos - 1)] xPosition]) {
         //the eb to the left is at the same x position as the eb to set so use it's date
         [eventToSet setBeginTime:[[[[sortedEBArray objectAtIndex:(eventsArrayPos - 1)] beginTime] copy] autorelease]];
         [eventToSet setBeginTimeStatus:YES];
         return;
      }
   }

   if(![eventToSet isEqualTo:[sortedEBArray lastObject]]) {
      if([eventToSet xPosition] == [[sortedEBArray objectAtIndex:(eventsArrayPos + 1)] xPosition]) {
         //the eb to the right is at the same x position as the eb to set so use it's date
         [eventToSet setBeginTime:[[[[sortedEBArray objectAtIndex:(eventsArrayPos + 1)] beginTime] copy] autorelease]];
         [eventToSet setBeginTimeStatus:YES];
         return;
      }
   }

   if(![eventToSet isEqualTo:[sortedEBArray lastObject]]) {
      //increment 1 second over the right eb
      [eventToSet setBeginTime:(SSChartDate *)[[[sortedEBArray objectAtIndex:(eventsArrayPos + 1)] beginTime] dateByAddingYears:0 months:0 days:0 hours:0 minutes:0 seconds:-1]];
      [eventToSet setBeginTimeStatus:YES];
   }

   [chartView setNeedsDisplayInRect:[chartView visibleRect]];
}

//***********************************
- (BOOL)canZoomIn
{
   if([self chartViewScale] <= 1) {
      return NO;
   } else {
      return YES;
   }
}

- (BOOL)canZoomOut
{
   if([self chartViewScale] >= 2) {
      return NO;
   } else {
      return YES;
   }
}

- (BOOL)canZoomOverlapIn
{
   if([self displayMode] == SSOverlapDisplayMode) {
      return YES;
   }

   return NO;
}

- (BOOL)canZoomOverlapOut
{
   if([self displayMode] == SSOverlapDisplayMode) {
      if([self overlapPixelsPerUnit] > SSOverlapPixelsForZoom) {
         return YES;
      }
   }

   return NO;
}

- (void)zoomIn
{
   NSSize newUnitSize = NSMakeSize(1.111111, 1.111111);

   [chartView scaleUnitSquareToSize:newUnitSize];
   [tableView scaleUnitSquareToSize:newUnitSize];
   [dateDisplayView scaleUnitSquareToSize:newUnitSize];

   [tableView sizeLastColumnToFit];
   [chartView setNeedsDisplay:YES];
   [tableView setNeedsDisplay:YES];
   
}

- (void)zoomOut
{
   NSSize newUnitSize = NSMakeSize(0.9, 0.9);

   [chartView scaleUnitSquareToSize:newUnitSize];
   [tableView scaleUnitSquareToSize:newUnitSize];
   [dateDisplayView scaleUnitSquareToSize:newUnitSize];

   [tableView sizeLastColumnToFit];
   [chartView setNeedsDisplay:YES];
   [tableView setNeedsDisplay:YES];
}

- (void)zoomOverlapIn
{
   [chartView zoomOverlapIn];
}

- (void)zoomOverlapOut
{
   [chartView zoomOverlapOut];
}

- (float)chartViewScale
{
   return [chartView convertSize:NSMakeSize(1,1) fromView:nil].width;
}

- (float)overlapUnit
{
   return [chartView overlapUnit];
}

- (void)setOverlapUnit:(float)unit
{
   [chartView setOverlapUnit:unit];
}

- (float)overlapPixelsPerUnit
{
   return [chartView overlapPixelsPerUnit];
}

- (void)setOverlapPixelsPerUnit:(float)pix
{
   [chartView setOverlapPixelsPerUnit:pix];
}

- (int)displayMode
{
   return [chartView displayMode];
}

- (void)setDisplayMode:(int)mode
{
   if(mode == SSMatrixDisplayMode) {
      [[SSOverlapScaleAndRangePanelController sharedOverlapScaleAndRangePanelController] close];
      [self prepareViewsForMatrixDisplay];
   } else {
      [[SSObjectFilterPanelController sharedObjectFilterPanelController] close];
      [self prepareViewsForOverlapDisplay];
   }
   
   [chartView setDisplayMode:mode];
}

- (SSDateDisplayView *)dateDisplayView
{
   return dateDisplayView;
}

- (SSChartView *)chartView
{
   return chartView;
}

// ============================================================
// NSToolbar Related Methods
// ============================================================

- (NSToolbarItem *) toolbar: (NSToolbar *)toolbar itemForItemIdentifier: (NSString *) itemIdent willBeInsertedIntoToolbar:(BOOL) willBeInserted {
   NSToolbarItem *toolbarItem = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdent] autorelease];

   if ([itemIdent isEqual:SSNewEBToolbarItemIdentifier]) {
      // Set the text label to be displayed in the toolbar and customization palette
      [toolbarItem setLabel:NSLocalizedString(@"NewEB", nil)];
      [toolbarItem setPaletteLabel:NSLocalizedString(@"NewEB", nil)];

      [toolbarItem setToolTip:NSLocalizedString(@"ToolbarNewEBTip", nil)];
      [toolbarItem setImage:[NSImage imageNamed: @"newRecord.tiff"]];

      // Tell the item what message to send when it is clicked
      [toolbarItem setTarget: self];
      [toolbarItem setAction: @selector(showNewEBPanel)];
   } else if([itemIdent isEqual:SSNewAlertCommentToolbarItemIdentifier]) {
      // Set the text label to be displayed in the toolbar and customization palette
      [toolbarItem setLabel:NSLocalizedString(@"NewAlertComment", nil)];
      [toolbarItem setPaletteLabel:NSLocalizedString(@"NewAlertComment", nil)];

      [toolbarItem setToolTip:NSLocalizedString(@"NewAlertComment", nil)];
      [toolbarItem setImage:[NSImage imageNamed: @"newAlertComment.tiff"]];

      // Tell the item what message to send when it is clicked
      [toolbarItem setTarget: self];
      [toolbarItem setAction: @selector(newAlertComment)];
   } else if([itemIdent isEqual:SSNewNoteCommentToolbarItemIdentifier]) {
      // Set the text label to be displayed in the toolbar and customization palette
      [toolbarItem setLabel:NSLocalizedString(@"NewNoteComment", nil)];
      [toolbarItem setPaletteLabel:NSLocalizedString(@"NewNoteComment", nil)];

      [toolbarItem setToolTip:NSLocalizedString(@"NewNoteComment", nil)];
      [toolbarItem setImage:[NSImage imageNamed: @"newNoteComment.tiff"]];

      // Tell the item what message to send when it is clicked
      [toolbarItem setTarget: self];
      [toolbarItem setAction: @selector(newNoteComment)];
   } else if([itemIdent isEqual:SSNewQuestionCommentToolbarItemIdentifier]) {
      // Set the text label to be displayed in the toolbar and customization palette
      [toolbarItem setLabel:NSLocalizedString(@"NewQuestionComment", nil)];
      [toolbarItem setPaletteLabel:NSLocalizedString(@"NewQuestionComment", nil)];

      [toolbarItem setToolTip:NSLocalizedString(@"NewQuestionComment", nil)];
      [toolbarItem setImage:[NSImage imageNamed: @"newQuestionComment.tiff"]];

      // Tell the item what message to send when it is clicked
      [toolbarItem setTarget: self];
      [toolbarItem setAction: @selector(newQuestionComment)];
   } else if([itemIdent isEqual:SSSnapChartToTimesToolbarItemIdentifier]) {
      // Set the text label to be displayed in the toolbar and customization palette
      [toolbarItem setLabel:NSLocalizedString(@"SnapEBsToTime", nil)];
      [toolbarItem setPaletteLabel:NSLocalizedString(@"SnapEBsToTime", nil)];

      [toolbarItem setToolTip:NSLocalizedString(@"SnapEBsToTimeTip", nil)];
      [toolbarItem setImage:[NSImage imageNamed: @"snapChartToTimes.tiff"]];

      // Tell the item what message to send when it is clicked
      [toolbarItem setTarget: self];
      [toolbarItem setAction: @selector(snapChartToTimes)];
   } else if([itemIdent isEqual:SSSnapTimesToChartToolbarItemIdentifier]) {
      // Set the text label to be displayed in the toolbar and customization palette
      [toolbarItem setLabel:NSLocalizedString(@"SnapTimesToMatrix", nil)];
      [toolbarItem setPaletteLabel:NSLocalizedString(@"SnapTimesToMatrix", nil)];

      [toolbarItem setToolTip:NSLocalizedString(@"SnapTimesToMatrixTip", nil)];
      [toolbarItem setImage:[NSImage imageNamed: @"snapTimesToChart.tiff"]];

      // Tell the item what message to send when it is clicked
      [toolbarItem setTarget: self];
      [toolbarItem setAction: @selector(snapTimesToChart)];
   } else if([itemIdent isEqual:SSDeleteSelectedToolbarItemIdentifier]) {
      // Set the text label to be displayed in the toolbar and customization palette
      [toolbarItem setLabel:NSLocalizedString(@"DeleteSelected", nil)];
      [toolbarItem setPaletteLabel:NSLocalizedString(@"DeleteSelected", nil)];

      [toolbarItem setToolTip:NSLocalizedString(@"DeleteSelected", nil)];
      [toolbarItem setImage:[NSImage imageNamed: @"deleteSelected.tiff"]];

      // Tell the item what message to send when it is clicked
      [toolbarItem setTarget: self];
      [toolbarItem setAction: @selector(deleteSelectedObjects)];
   } else if([itemIdent isEqual:SSExportToolbarItemIdentifier]) {
      // Set the text label to be displayed in the toolbar and customization palette
      [toolbarItem setLabel:NSLocalizedString(@"ExportToolbar", nil)];
      [toolbarItem setPaletteLabel:NSLocalizedString(@"ExportToolbar", nil)];

      [toolbarItem setToolTip:NSLocalizedString(@"ExportToolbar", nil)];
      [toolbarItem setImage:[NSImage imageNamed: @"export.tiff"]];

      // Tell the item what message to send when it is clicked
      [toolbarItem setTarget: self];
      [toolbarItem setAction: @selector(sendExportNote)];
   } else if([itemIdent isEqual:SSInspectorToolbarItemIdentifier]) {
      // Set the text label to be displayed in the toolbar and customization palette
      [toolbarItem setLabel:NSLocalizedString(@"InspectorToolbar", nil)];
      [toolbarItem setPaletteLabel:NSLocalizedString(@"InspectorToolbar", nil)];

      [toolbarItem setToolTip:NSLocalizedString(@"InspectorToolbar", nil)];
      [toolbarItem setImage:[NSImage imageNamed: @"inspector.tiff"]];

      // Tell the item what message to send when it is clicked
      [toolbarItem setTarget: self];
      [toolbarItem setAction: @selector(showInspectorPanel)];
   } else if([itemIdent isEqual:SSNeedsDiamondToolbarItemIdentifier]) {
      // Set the text label to be displayed in the toolbar and customization palette
      [toolbarItem setLabel:NSLocalizedString(@"DiamondToolbar", nil)];
      [toolbarItem setPaletteLabel:NSLocalizedString(@"DiamondToolbar", nil)];

      [toolbarItem setToolTip:NSLocalizedString(@"DiamondToolbar", nil)];
      [toolbarItem setImage:[NSImage imageNamed: @"newDiamond.tiff"]];

      // Tell the item what message to send when it is clicked
      [toolbarItem setTarget: self];
      [toolbarItem setAction: @selector(addNeedsDiamondToSelectedObject)];
   } else if([itemIdent isEqual:SSFilterToolbarItemIdentifier]) {
      // Set the text label to be displayed in the toolbar and customization palette
      [toolbarItem setLabel:NSLocalizedString(@"DateRangeFilter", nil)];
      [toolbarItem setPaletteLabel:NSLocalizedString(@"DateRangeFilter", nil)];

      [toolbarItem setToolTip:NSLocalizedString(@"DateRangeFilterTip", nil)];
      [toolbarItem setImage:[NSImage imageNamed: @"dateRangeFilter.tiff"]];

      // Tell the item what message to send when it is clicked
      [toolbarItem setTarget: self];
      [toolbarItem setAction: @selector(showObjectFilterPanel)];
   } else if([itemIdent isEqual:SSZoomInToolbarItemIdentifier]) {
      // Set the text label to be displayed in the toolbar and customization palette
      [toolbarItem setLabel:NSLocalizedString(@"ZoomIn", nil)];
      [toolbarItem setPaletteLabel:NSLocalizedString(@"ZoomIn", nil)];

      [toolbarItem setToolTip:NSLocalizedString(@"ZoomIn", nil)];
      [toolbarItem setImage:[NSImage imageNamed: @"zoomIn.tiff"]];

      // Tell the item what message to send when it is clicked
      [toolbarItem setTarget: self];
      [toolbarItem setAction: @selector(zoomIn)];
   } else if([itemIdent isEqual:SSZoomOutToolbarItemIdentifier]) {
      // Set the text label to be displayed in the toolbar and customization palette
      [toolbarItem setLabel:NSLocalizedString(@"ZoomOut", nil)];
      [toolbarItem setPaletteLabel:NSLocalizedString(@"ZoomOut", nil)];

      [toolbarItem setToolTip:NSLocalizedString(@"ZoomOut", nil)];
      [toolbarItem setImage:[NSImage imageNamed: @"zoomOut.tiff"]];

      // Tell the item what message to send when it is clicked
      [toolbarItem setTarget: self];
      [toolbarItem setAction: @selector(zoomOut)];
   } else if([itemIdent isEqual:SSSwitchViewTypeToolbarItemIdentifier]) {
      // Set the text label to be displayed in the toolbar and customization palette
      [toolbarItem setLabel:NSLocalizedString(@"SwitchViewType", nil)];
      [toolbarItem setPaletteLabel:NSLocalizedString(@"SwitchViewType", nil)];

      [toolbarItem setToolTip:NSLocalizedString(@"SwitchViewTypeTip", nil)];
      [toolbarItem setImage:[NSImage imageNamed: @"switchViewType.tiff"]];

      // Tell the item what message to send when it is clicked
      [toolbarItem setTarget: self];
      [toolbarItem setAction: @selector(switchDisplayMode)];
   } else if([itemIdent isEqual:SSZoomOutOverlapToolbarItemIdentifier]) {
      // Set the text label to be displayed in the toolbar and customization palette
      [toolbarItem setLabel:NSLocalizedString(@"DecreaseOverlapScale", nil)];
      [toolbarItem setPaletteLabel:NSLocalizedString(@"DecreaseOverlapScale", nil)];

      [toolbarItem setToolTip:NSLocalizedString(@"DecreaseOverlapScaleTip", nil)];
      [toolbarItem setImage:[NSImage imageNamed: @"decreaseOverlapScale.tiff"]];

      // Tell the item what message to send when it is clicked
      [toolbarItem setTarget: self];
      [toolbarItem setAction: @selector(zoomOverlapOut)];
   } else if([itemIdent isEqual:SSZoomInOverlapToolbarItemIdentifier]) {
      // Set the text label to be displayed in the toolbar and customization palette
      [toolbarItem setLabel:NSLocalizedString(@"IncreaseOverlapScale", nil)];
      [toolbarItem setPaletteLabel:NSLocalizedString(@"IncreaseOverlapScale", nil)];

      [toolbarItem setToolTip:NSLocalizedString(@"IncreaseOverlapScaleTip", nil)];
      [toolbarItem setImage:[NSImage imageNamed: @"increaseOverlapScale.tiff"]];

      // Tell the item what message to send when it is clicked
      [toolbarItem setTarget: self];
      [toolbarItem setAction: @selector(zoomOverlapIn)];      
   }

   return toolbarItem;
}

- (void)setupToolbar {
   // Create a new toolbar instance, and attach it to our document window
   NSToolbar *toolbar = [[[NSToolbar alloc] initWithIdentifier: SSCatalystDocToolbarIdentifier] autorelease];

   // Set up toolbar properties: Allow customization, give a default display mode, and remember state in user defaults
   [toolbar setAllowsUserCustomization: YES];
   [toolbar setAutosavesConfiguration: YES];
   [toolbar setDisplayMode:NSToolbarDisplayModeIconOnly];

   // We are the delegate
   [toolbar setDelegate:self];

   // Attach the toolbar to the document window
   [[[[self windowControllers] objectAtIndex:0] window] setToolbar:toolbar];
}

- (NSArray *) toolbarDefaultItemIdentifiers: (NSToolbar *) toolbar {
   // Required delegate method   Returns the ordered list of items to be shown in the toolbar by default
   // If during the toolbar's initialization, no overriding values are found in the user defaults, or if the
   // user chooses to revert to the default items self set will be used
   return [NSArray arrayWithObjects:SSNewEBToolbarItemIdentifier, SSDeleteSelectedToolbarItemIdentifier, NSToolbarSpaceItemIdentifier,
       SSNeedsDiamondToolbarItemIdentifier, SSNewNoteCommentToolbarItemIdentifier,
      NSToolbarSpaceItemIdentifier, SSSnapChartToTimesToolbarItemIdentifier, SSSnapTimesToChartToolbarItemIdentifier,
      NSToolbarSpaceItemIdentifier,
      SSInspectorToolbarItemIdentifier, NSToolbarSpaceItemIdentifier, SSExportToolbarItemIdentifier, nil];
}

- (NSArray *) toolbarAllowedItemIdentifiers: (NSToolbar *) toolbar {
   // Required delegate method   Returns the list of all allowed items by identifier   By default, the toolbar
   // does not assume any items are allowed, even the separator   So, every allowed item must be explicitly listed
   // The set of allowed items is used to construct the customization palette
   return [NSArray arrayWithObjects:NSToolbarPrintItemIdentifier, NSToolbarCustomizeToolbarItemIdentifier,
      NSToolbarFlexibleSpaceItemIdentifier, NSToolbarSpaceItemIdentifier, NSToolbarSeparatorItemIdentifier,
      NSToolbarShowColorsItemIdentifier, NSToolbarShowFontsItemIdentifier, SSNewNoteCommentToolbarItemIdentifier,
      SSNewAlertCommentToolbarItemIdentifier, SSNewQuestionCommentToolbarItemIdentifier, SSSnapChartToTimesToolbarItemIdentifier,
      SSSnapTimesToChartToolbarItemIdentifier, SSDeleteSelectedToolbarItemIdentifier, SSExportToolbarItemIdentifier,
      SSInspectorToolbarItemIdentifier, SSNeedsDiamondToolbarItemIdentifier,
      SSZoomInToolbarItemIdentifier, SSZoomOutToolbarItemIdentifier, SSFilterToolbarItemIdentifier,
      SSSwitchViewTypeToolbarItemIdentifier, SSZoomInOverlapToolbarItemIdentifier, SSZoomOutOverlapToolbarItemIdentifier, nil];
}

- (BOOL)validateToolbarItem:(NSToolbarItem *)toolbarItem {
   BOOL enable = YES;

   if ([[toolbarItem itemIdentifier] isEqual:SSNeedsDiamondToolbarItemIdentifier]) {
      if(![self canAddNeedsDiamond]) {
         return NO;
      }
   }

   if ([[toolbarItem itemIdentifier] isEqual:SSNewNoteCommentToolbarItemIdentifier]) {
      if([self displayMode] != SSMatrixDisplayMode) {
         return NO;
      }
   }

   if ([[toolbarItem itemIdentifier] isEqual:SSNewAlertCommentToolbarItemIdentifier]) {
      if([self displayMode] != SSMatrixDisplayMode) {
         return NO;
      }
   }

   if ([[toolbarItem itemIdentifier] isEqual:SSNewQuestionCommentToolbarItemIdentifier]) {
      if([self displayMode] != SSMatrixDisplayMode) {
         return NO;
      }
   }

   if ([[toolbarItem itemIdentifier] isEqual:SSDeleteSelectedToolbarItemIdentifier]) {
      if([[self selectedObjects] count] == 0) {
         enable = NO; //no objects selected
      }
   }
   
   if ([[toolbarItem itemIdentifier] isEqual:SSZoomInToolbarItemIdentifier]) {
      if(![self canZoomIn]) {
         enable = NO; 
      }
   }

   if ([[toolbarItem itemIdentifier] isEqual:SSZoomOutToolbarItemIdentifier]) {
      if(![self canZoomOut]) {
         enable = NO;
      }
   }

   if ([[toolbarItem itemIdentifier] isEqual:SSSnapTimesToChartToolbarItemIdentifier]) {
      if([self displayMode] != SSMatrixDisplayMode) {
         enable = NO;
      }
   }

   if ([[toolbarItem itemIdentifier] isEqual:SSSnapChartToTimesToolbarItemIdentifier]) {
      if([self displayMode] != SSMatrixDisplayMode) {
         enable = NO;
      }
   }

   if ([[toolbarItem itemIdentifier] isEqual:SSZoomInOverlapToolbarItemIdentifier]) {
      if(![self canZoomOverlapIn]) {
         enable = NO;
      }
   }

   if ([[toolbarItem itemIdentifier] isEqual:SSZoomOutOverlapToolbarItemIdentifier]) {
      if(![self canZoomOverlapOut]) {
         enable = NO;
      }
   }
   
   return enable;
}

//***********************************

- (void)dealloc
{
   [[NSNotificationCenter defaultCenter] removeObserver:self];
   [documentData release];
   [missingFiles release];
   [actorOrder release];
   [jumpMapView release];
   [skippedImportedEvents release];
   [objectFilterController release];
   
   [chartView setTheDocument:nil]; //so that chartView doesn't try to call us as it is deallocating
   
   [super dealloc];
}


@end